为什么HashMap扩容是2倍以及容量为什么是2的n次幂
** java8**
为什么HashMap扩容是2倍以及容量为什么是2的n次幂,和这个数组下标的计算方法有着千丝万缕的关系。
先看看计算数组下标源码:
由上图我们可以看到,<key,value>要放到数组的那个位置,它会通过key的hash值和数组长度-1进行与运算来计算得出。也就是
(n - 1) & hash
这里n就是数组长度。 可以看出一旦数组扩容,算式中n就发生了变化,那么原来元素下标也会发生变化,<key,value>就要移动位置。
如果每一次扩容所有的<key,value>都要移动,势必带来性能上的不足。
但是当扩容是2倍,我们就会发现非常有趣的地方。
我们来看一下hashmap的扩容过程。
16(默认)-> 32(扩容一次) -> 64(扩容两次)
从10进制中我们或许发现不了什么,但是它是进行与运算所以咱们只要二进制,这时就有点意思了。
0001 0000 //16二进制
0010 0000 //32二进制
0100 0000 //64二进制
-1之后则变为
0000 1111
0001 1111
0011 1111
比如现在我们有三个<key,value>要插入,通过计算key的hash值分别为
0101 1010 0011 1101
0101 1010 0011 1101
1110 0100 0001 0001
代入计算之后
hashCode(计算的hash值只用高16) | size=16 | size=32 | 是否移位 |
---|---|---|---|
1010 0011 1101 | 1010 0011 1101&1111 | 1010 0011 1101&11111 | 向前移动16位 |
1010 0011 1101 | 1010 0011 1101&1111 | 1010 0011 1101&11111 | 不用移位 |
0100 0001 0001 | 0100 0001 0001&1111 | 0100 0001 0001&11111 | 向前移动16位 |
我们发现,扩容后是否移位,由扩容后key的hashcode参与计算的最高位是否1为所决定,并且移动的方向只有一个,即向高位移动。因此,可以根据对最高位进行检测的结果来决定是否移位,从而可以优化性能,不用每一个元素都进行移位,
是否移位,由扩容后表示的最高位是否1为所决定,并且移动的方向只有一个,即向高位移动。因此,可以根据对最高位进行检测的结果来决定是否移位,从而可以优化性能,不用每一个元素都进行移位。最高位为0 与运算之后的值依然与之前相同说明不用向高位移动。为1则需要向高位移动。
结论,原因有二:#
第一哈希函数的问题 将元素充分散列,避免不必要的哈希冲突。
通过除留余数法方式获取桶号,因为Hash表的大小始终为2的n次幂,因此可以将取模转为位运算操作,提高效率,容量n为2的幂次方,n-1的二进制会全为1,位运算时可以充分散列,避免不必要的哈希冲突。
第二就是扩容时桶内元素是否向高位移动的问题。扩容为2倍,参与计算的hashcode高位为1则移动,为0则不移动,也就是说一个桶内元素只有一半的概率需要移动,从而优化了性能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构