java的hashMap扩容新地址计算的巧妙
what:
hashmap扩容
1、重新建立一个新的数组,长度为原数组的两倍(实际长度为2的n次幂);
2、遍历旧数组的每个数据,重新计算每个元素在新数组中的存储位置(一次性完成);使用节点的hash值与旧数组长度进行位与运算,如果运算结果为0,表示元素在新数组中的位置不变;否则,则在新数组中的位置下标=原位置+原数组长度。
why:
为什么扩容时节点重 hash 只可能分布在原索引位置或者 原索引长度+oldCap 位置呢?换句话说,扩容时使用节点的hash值跟oldCap进行位与运算,以此决定将节点分布到原索引位置或者原索引+oldCap位置上的原理是什么呢?
假设老表的容量为16,则新表容量为16*2=32,假设节点1的hash值为 0000 0000 0000 0000 0000 1111 0000 1010,节点2的hash值为 0000 0000 0000 0000 0000 1111 0001 1010。
那么节点1和节点2在老表的索引位置计算如下图:
计算1(16个桶的真实计算),由于老表的长度限制,节点1和节点2的索引位置只取决于节点hash值的最后4位。
计算2(32个桶的真实计算,扩容1倍后),计算2为元素在新表中的索引计算,可以看出如果两个节点在老表的索引位置相同,则新表的索引位置只取决于节点hash值倒数第5位的值,而此位置的值刚好为老表的容量值16,此时节点在新表的索引位置只有两种情况:原索引位置和 原索引+oldCap(放大1倍)位置(在此例中即为10和10+16=26)。
由于结果只取决于节点hash值的倒数第5位,而此位置的值刚好为老表的容量值16,因此此时新表的索引位置的计算可以替换为计算3,直接使用节点的hash值与老表的容量16进行位于运算,如果结果为0则该节点在新表的索引位置为原索引位置,否则该节点在新表的索引位置为 原索引+ oldCap 位置。