我说HashMap初始容量是16,面试官让我回去等通知( 二 )

4. HashMap容量大小为什么要设置成2的倍数?int index = hash(key) & (n-1);为了更快的计算key所在的数组下标位置 。
当数组长度(n)是2的倍数的时候,就可以直接通过逻辑与运算(&)计算下标位置,比取模速度更快 。
5. HashMap为什么是线程不安全?原因就是HashMap的所有修改方法都没有加锁,导致在多线程情况下,无法保证数据一致性和安全性 。
比如:一个线程删除了一个key,由于没有加锁,其他线程无法及时感知到,还继续能查到这个key,无法保证数据的一致性 。
比如:一个线程添加完一个元素,由于没有加锁,其他线程无法及时感知到,另一个线程正在扩容,扩容后就把上一个线程添加的元素弄丢了,无法保证数据的安全性 。
6. 解决哈希冲突方法有哪些?常见有链地址法、线性探测法、再哈希法等 。

  • 链地址法
    就是把发生哈希冲突的值组成一个链表,HashMap就是采用的这种 。
  • 线性探测法
    发生哈希冲突后,就继续向下遍历,直到找到空闲的位置,ThreadLocal就是采用的这种,以后再详细讲 。
  • 再哈希法
    使用一种哈希算法发生了冲突,就换一种哈希算法,直到不冲突为止(就是这么聪明) 。
7. JDK1.8扩容流程有什么优化?在JDK1.7扩容的时候,会遍历原数组,重新哈希,对新数组长度逻辑与,计算出数据下标,然后放到新数组中,比较麻烦耗时 。
在JDK1.8扩容的时候,会遍历原数组,然后统计出两组数据,一组是新数组的下标位置不变,另一组是新数组的下标位置等于原数组的下标位置加上原数组的长度 。
比如:数组长度由16扩容到32,哈希值是0和32的元素,在新旧数组中下标位置不变,都是下标为0的位置 。而哈希值是16和48的元素,在新数组的位置=原数组的下标+原数组的长度,也就是下标为16的位置 。
我说HashMap初始容量是16,面试官让我回去等通知

文章插图

我说HashMap初始容量是16,面试官让我回去等通知

文章插图

经验总结扩展阅读