hashmap的实现原理(参考



要点一:为什么length是2的幂次方??

1、hashMap的存储原理是将key的hash值跟 length-1 进行 & 运算(十进制是%求模运算),从而求得key在hashmap 数组中的位置。
2的幂次方可以减少冲突(碰撞),提高hashMap的查询效率。

注:碰撞即不同的KEY值求出来的存储位置相同,这种情况叫hash碰撞(冲突),解决碰撞的方法是:链地址法,即将碰撞的oldvalue存到Entry.next中(链表中)

2、如果length为2的n次幂,则length-1为11111……的形式,在同hash值的二进制做 & 运算时效率会非常的快,而且空间不浪费。
3、如果length不为2的n次幂,比如length为15,则length-1为14,二进制为1110,在同hash值&运算时,运算结果最后一位都为0的hash值永远不能存放元素
如0001,0011,0101,1001,0111,1011,1101。空间浪费很大。且数组可使用的位置数比数组长度(length)小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率。


要点二:hashmap是如何扩容的

加载因子:0.75
容量:1<<4  (即二进制10000,十进制为16)
极限值:1<<30  超过这个值,
则将 threshold修改为Integer.MAX_VALUE(此时桶大小已经是2的31次方了),表明不进行扩容了)

初始化hashmap后: 扩容上限threshold=0.75*16=12,即当桶中键值对的个数超过12时就进行扩容(数组被占用的个数,不一定是实际的键值对个数)

Java7扩容时,遍历每个节点,并重新hash获得当前数组的位置并添加到链表中;Java8进一步做了优化,将元素的hash和旧数组的大小(大小为2次幂)做与运算,为0则表示数组位置不变,不为0则表示需要移位,新位置为 原先位置+旧数组的小大 (新数组大小为旧数组翻倍),并将当前链表拆分为两个链表,一个链表放到原先位置,一个链路放到新位置,效率比Java7高。
额外提一点,Java的链表节点数超过8个时,会将链表转化为红黑树,当hash命中很低时,效率比Java7高很多。

举例 容量16 ,二进制表示为10000 ,当hash值为11000同 length-1  1111做 & 运算时,得到的存储位置为01000
扩容后容量为32,二进制表示为100000,同样的hash值11000跟length-1 11111做&运算,得到的存储位置为11000,跟扩容前不同
新位置=原先位置+旧数组的大小=01000+10000=11000

要点三:hashmap的key值可以为null,且为key为null时,存储的位置为0








猜你喜欢

转载自blog.csdn.net/hh1sdfsf56456/article/details/79449847