Java学习之java中的HashMap是什么

  HashMap

 初始容量是16,负载因子0.75。

  HashMap内部是通过数组+链表的形式存储数据,1.8当链表长度大于8时会进化为红黑树,小于6时会退化为链表。红黑树是一种平衡二叉树,能够保证查询的时间复杂度在log(N)。

  负载因子是指存储的键值对数和容量的比值。因此有一个阈值(threshold)的概念,阈值=容量×负载因子。当存储一个键值对时,键值对的数量已经达到阈值,而且根据hash值判断对应的数组位置上也已经有值,就会执行扩容操作。

 

  HashMap通过计算key的hash值来判断要将键值对存储在数组的哪个位置上,通过拉链法解决hash碰撞。(常见hash碰撞的解决办法:开放地址法、再哈希、拉链法)

 

  jdk1.7  

HashMap:key如果为null默认hash值为0。如果判断数组对应位置上没有键值对就直接插入;否则有可能已经有相同的key了,通过e.hashhash(key)&&

 

(e.keykey||e.key.equals(key)来判断,如果是同一个key那么替换对应Entry的值;否则说明不存在相同的key,使用头插法插入到链表中,这会导致扩容时出现并发问题。当键值对数量达到阈值而且对应数组位置上有值,就会执行扩容。扩容操作会将容量乘以2倍(容量永远是2的幂次),然后分配一个新的数组,遍历原来的老数组中的每一个节点,重新计算在新数组中的位置,也使用头插法插入到新数组对应位置的链表中,然后将新数组赋值给内部数组的引用。然后再进行插入键值对的操作。

 

  jdk1.8 HashMap:同样,null  

 

key的hash值为0;如果数组对应位置没有值就直接插入;否则通过

e.hashhash(key)&&(e.keykey||e.key.equals(key)来判断如果是同一个key那么替换对应的值;如果这里是链表结构,采用尾插法插入数据,否则为红黑树结构,向红黑树插入结点。如果在链表插入数据后达到了红黑树阈值,那么就把链表转化为红黑树。然后将数组size增加,判断是否已经达到阈值,如果达到了那么进行扩容操作。**这里可以看到1.7和1.8的不同:除了头插法和尾插法的区别,还有1.8是先增加键值对再扩容,1.7恰恰相反。**扩容时,如果当前是一条链表,那么通过(e.hash  

& oldCap) ==  

0来判断这个节点是要放在同一个位置,还是这个位置+oldCap上。使用两条链表lo和hi来表示放在原位置和由于扩容放在新位置上的链表,然后在执行插入,这里都使用尾插法,因此不会出现1.7中死循环的问题。

 

【面试题】HashMap 1.7和1.8的实现有什么不同:

 

  【解答】 (1) 存储结构不同,1.7是数组+链表,1.8是数组+链表+红黑树;(2)  

扩容转移数据的方式不同,1.7采用头插法,1.8是尾插法,而且达到转化红黑树的阈值要转化为红黑树;(3)  扩容计算元素存储位置的方式不同,1.7是直接用hash&(newCap-1)计算,1.8是通过hash&oldCap==0来判断,1.7是一个一个键值对移动,1.8是先排列成两个链表然后直接把链表转移过去;(4)  扩容和增加键值对顺序不同,1.7是先扩容后增加键值对,1.8是先增加键值对后扩容。

 

 【面试题】HashMap的容量为什么是2的幂次?

 

 【解答】当容量是2的幂次时,n-1对应的掩码是连续的1,可以保证数据均匀分布。

posted @ 2021-11-02 14:43  指尖上的代码go  阅读(86)  评论(0)    收藏  举报