• 什么是hashMap?

这里分两种:

jdk1.7以前,他是基于数组+链表来实现的,使用链表来处理冲突(头插法),这样在同一个hash值得数据比较多的情况会产生查询变慢的一个问题
【面试考点看这一篇就够了】HashMap
【面试考点看这一篇就够了】HashMap
【面试考点看这一篇就够了】HashMap

所以在jdk1.8以后,对hashMap进行了一个调优,即使用数组+链表+红黑树来实现(尾部插入),实际上就是对查找的速度实行了一个优化,当链表长度超过阈值(8)之后,我们就会把链表转化成为红黑树

为什么链表长度超过8之后我们才把链表转化成为红黑树?
因为红黑树的平均查找长度是log(n),链表的平均查找长度是n/2,长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是转化为树结构和生成树的时间并不会太短。

  • hashmap的默认初始长度是多少?为什么是这个数字?

hashmap的默认初始长度是16,默认的加载因子是0.75

这里涉及到hashmap的扩容(resize())的问题,在构造hash表的时候,如果没有指明初始的大小,默认大小为16,如果Node【】数组中的的元素的个数高达一定程度的时候(加载因子*Node.length),就会去自动扩容,当然我们可以手动扩容,即重新调整hashmap的大小,变为原来的2倍。

hashmap在选取存储在哪个链表的时候,有一个hash算法。这个hash算法其实在我们看来就是对被存储的key执行一个取模运算。但是在计算机内部,他的计算都是通过补码来运算的,hashmap的hash函数运用的是位运算,即按位于

假设我们要插入的key为:3029737,转换成为二进制就是:10 1110 0011 1010 1110 1001 那我们默认的初始值为16,转换成为计算机的计数,就是length-1=15,二进制为:1111 当我们使用按位于的时候,得到的结果就是1001,即9,这个结果也就是我们的取模操作的结果

那如果我们选取的默认初始值为10的话,二进制为:1001
这样我们在转换二进制数:10 1110 0011 1010 1110 1101 和 10 1110 0011 1010 1110 1011的时候,得到的取模结果都是1001,这样出现的hash值就有很大概率上的重复了,这也就是我们为什么在扩容的时候会设置为当前值的2倍:

16 —— 1111
32 —— 1111 1111
64 —— 1111 1111 1111

  • 为什么要扩容?

我们在前面讲到了,当元素的数量多到一定程度的时候,链表就会很冗长,查找的速度就会变慢(当然了,jdk1.8之后,我们转换成为红黑树会减缓这个问题),所以我们将数组进行扩容,重新散列,加快我们的查找速度。

  • 在高并发的情况下为什么hashmap会发生死锁?

老生常谈,HashMap的死循环

  • hashmap是线程不安全的,那么我们要怎么去解决这个问题?

我们可以用ConcurrentHashMap和hashtable来解决这个问题,一般我们优先使用ConcurrentHashMap来解决这个问题

  • 解决线程不安全的问题,可以也使用hashtable,为什么我们要使用ConcurrentHashMap?

首先hashtable和concurrentHashMap都是线程安全的,但是在性能上ConcurrentHashMap更胜一筹。为什么这么说呢?因为hashtable在实现线程安全的时候,是将整个hashtable都用synchronized锁住,而concurrentHashMap只锁住了我们要操作的那一个segment,对其他的segment没有锁住,也就是可以同时操作多个segment同时又保证了线程安全。

【面试考点看这一篇就够了】HashMap

相关文章: