JDK1.8中对HashMap的优化

1.

       Jdk1.8中没有indexFor函数,直接使用table[index = (n – 1) & hash](与运算交换左右,结果不变)。其中table数组为HashMap解决哈希冲突的数组+链表法中的数组。

2.

       旧版本的HashMap存在一个问题,即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(TREEIFY_THRESHOLD默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能(O(logn))。当长度小于(UNTREEIFY_THRESHOLD默认为6),就会退化成链表。

3.

       经过观测可以发现,HashMap使用的是2次幂的扩容(指长度扩为原来2倍)。所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算(即hash()函数)结果。

《JDK1.8中对HashMap的优化》

扩容后,table长度(n)变为原来两倍,即n由
0000 0000 0000 0000 0000 0000 0001 0000
变为
0000 0000 0000 0000 0000 0000 0010 0000
,因此n-1由
0000 0000 0000 0000 0000 0000 0000 1111
变为
0000 0000 0000 0000 0000 0000 0001 1111

       元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:

《JDK1.8中对HashMap的优化》

       因此,我们在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”。(每个节点e的hash早就计算好,并保存在final hash中)。通过if ((e.hash & oldCap) == 0)判定前面那个bit是不是1,如果是1则加上oldCap。

4.

static class Node<K,V> implements Map.Entry<K,V> {}
jdk1.8中用Node替代了Entry。

    原文作者:小毛1221
    原文地址: https://www.jianshu.com/p/800aa1cf5c13
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞