HashMap扩容时的rehash方法中(e.hash & oldCap) == 0算法推导

PS:由于文档是我在本地编写好之后再复制过来的,有些文本格式没能完整的体现,故提供下述图片,供大家阅览,以便有更好的阅读体验:
在这里插入图片描述

HashMap在扩容时,需要先创建一个新数组,然后再将旧数组中的数据转移到新数组上来
此时,旧数组上的数据就会根据(e.hash & oldCap) 是否等于0这个算法,被很巧妙地分为2类:
① 等于0时,则将该头节点放到新数组时的索引位置等于其在旧数组时的索引位置,记为低位区链表lo开头-low;
② 不等于0时,则将该头节点放到新数组时的索引位置等于其在旧数组时的索引位置再加上旧数组长度,记为高位区链表hi开头high.
具体,详见下述的算法推导解析:
算法:
(e.hash & oldCap)=0
前提:
 e.hash代表的是旧数组中节点或元素或数据e的hash值,该hash值是根据key确定过的:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16) ;
 oldCap为旧数组的数组长度,是2的n次幂的整数。即e.hash&2^n=0

推导过程1(e.hash & oldCap)=0:

  1. 因为oldCap是2的n次幂的整数,其二进制表达为1个1后面跟n个0:1000…0,若想要e.hash&oldCap的结果为0,则e.hash的二进制形式中与对应oldCap的二进制的1的位置一定为0,其他位置的可以随意,这样即可保证结果为0;
  2. 假设:
    oldCap= 2 ^ 3 =8 = 1000
    则e.hash可以是 0101

e.hash&oldCap 0000=0
3. (2oldCap -1)=2 ^ 4-1=01111,其二进制位数比oldCap多一位,但多的这一位是0,其余都是1(其低三位肯定也是1);(oldCap-1)=2 ^ 3-1=0111,其二进制位数与oldCap相同,且其低3位的值都是1。故(2oldCap-1)和(oldCap -1)两者与只有4位且首位为0的e.hash=0101计算时,其实只有低3位真正能影响计算结果,而两者的低3位相同,都是111;
4. 故在前提条件下,(2oldCap-1)和(oldCap -1)两者与e.hash进行&运算之后的结果一样:
(2oldCap -1)=2 ^ 4-1= 01111 (oldCap-1)=2 ^ 3-1= 0111
e.hash 0101 e.hash 0101


e.hash&oldCap 00101=5 e.hash&oldCap 0101=5
5. 而(oldCap -1) &e.hash恰巧代表的就是e元素在旧数组中的索引位置;
而(2oldCap -1) &e.hash则代表的就是e元素在旧数组长度扩容2倍后的新数组里的索引位置
6. 综上,可得出满足e.hash&oldCap=0的元素,其在新旧数组中的索引位置不变;

推导过程2(e.hash & oldCap)不等于0:

  1. 因为oldCap是2的n次幂的整数,其二进制表达为1个1后面跟n个0:1000…0,若想要e.hash&oldCap的结果不为0,则e.hash的二进制形式中与对应oldCap的二进制的1的位置一定不为0,其他位置的可以随意,这样即可保证结果不为0;
  2. 假设:
    oldCap= 2 ^ 3 =8 = 1000
    则e.hash可以是 1101

e.hash&oldCap 1000=13
3. (2oldCap -1)=2 ^ 4-1=01111,其二进制位数比oldCap多一位,但多的这一位是0,其余都是1(其低三位肯定也是1,其从左到右数的第4位为1);(oldCap-1)=2 ^ 3-1=0111,其二进制位数与oldCap相同,且其低3位的值都是1, 其从左到右数的第4位为0,。故(2oldCap-1)和(oldCap -1)两者与只有4位且首位为1的e.hash=1101计算时,其实也只有从左到右数的第4位(0)真正能影响计算结果,因为低3位完全一样都是1;
4. 故在前提条件下,(2oldCap-1)和(oldCap -1)两者与e.hash进行&运算后结果相差了oldCap:
(2oldCap -1)=2^4-1= 01111 ( oldCap - 1 ) =2 ^ 3-1= 0111
e.hash 1101 e.hash 1101


(2oldCap -1)& e.hash 01101=8+5 (2oldCap -1)&e.hash 0101=5
5. 而(oldCap -1) &e.hash恰巧代表的就是e元素在旧数组中的索引位置;
而(2oldCap -1) &e.hash则代表的就是e元素在旧数组长度扩容2倍后的新数组里的索引位置
6. 综上,可得出满足e.hash&oldCap不等于0的元素,其在新数组中的索引位置是其在旧数组中索引位置的基础上再加上旧数组长度个偏移量。

  • 73
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 33
    评论
### 回答1: 在HashMap扩容,不需要重新计算hash值。在扩容HashMap会创建一个更大的数组,然后把原数组的元素重新分配到新数组。在这个过程HashMap会根据新数组的大小重新计算每个元素的索引位置,然后把元素放到新的位置上。这个过程,元素的hash值不会改变,只是重新计算了它的索引位置。 ### 回答2: 在HashMap扩容,不需要重新计算所有的hash值。 HashMap是通过hash值来确定元素在数组的位置的。当HashMap进行扩容,其内部会创建一个更大的数组,并将原有的元素重新放入新的数组。由于新的数组大小发生了改变,所以不能简单地将元素按原来的hash值放入新的数组,需要重新计算元素的hash值来确定在新数组的位置。 然而,并不需要重新计算所有的hash值。因为在HashMap扩容过程,并不会改变元素的hashCode值,hashCode值是不变的。只需要根据新数组的大小重新计算每个元素在新数组的位置即可。 扩容HashMap会将原数组的每个元素取出,重新计算其在新数组的位置,然后将其放入新数组对应的位置。这个位置的计算方法是:使用元素的hashCode值与新数组的长度进行取模运算,以确定元素在新数组的位置。 这样做的目的是为了保证HashMap能够在扩容后仍然能够保持较低的冲突率,实现较好的性能。因为新数组的长度更大,分布更均匀,可以降低元素之间发生冲突的概率。 总结来说,在HashMap扩容,并不需要重新计算所有的hash值,只需要重新计算每个元素在新数组的位置即可。这样可以保证在扩容后,HashMap仍然能够保持较好的性能和较低的冲突率。 ### 回答3: 在HashMap进行扩容,是需要重新计算hash值的。 HashMap扩容是通过增加桶的数量来实现的。当HashMap的元素数量超过了负载因子(默认为0.75)与当前桶数量的乘积,就会触发扩容操作。扩容会创建一个新的桶数组,并将原始桶数组的元素重新分配到新桶数组。 在重新分配元素,需要计算每个元素的新的hash值。原始桶数组的节点,根据其原始hash值的高位和新桶数量的与运算结果来确定新的桶的索引位置。通过重新计算hash值,可以保证在新的桶数组,元素能够均匀地分布在不同的桶,避免出现过多的冲突,提高HashMap在查找、插入和删除操作上的性能。 因此,在HashMap进行扩容,需要重新计算hash值,以实现元素的均匀分布。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dylanioucn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值