Java-取模操作中的&和(length-1)、HashMap取模、HashMap扩容
1.背景
在哈希表相关的操作中,有一个典型的问题。
现在,我们假定数组的长度为8,元素个数为10个。
如果我们拥有一个理想的哈希函数,可以将其中8个元素均匀的放置到数组的8个位置上。
不过由于有10个元素,所以必然会出现冲突的可能。
2.演示
2.1 取模:直接%
现在,我们模拟下上面的操作,选择怎么设计一个哈希函数。
由于值是已知的,很显然能想到一个办法,直接取模。
运行结果
现在,给定的元素都能有序的放置到数组中。
2.2 hashCode()函数
上面的例子比较简单,我们换一下,假定给出的元素是。
嗯,现在思路就乱了,没有任何规律呀?
所以,我们可以想到使用jdk自带的哈希函数,先来打印一下哈希值。
运行结果
为了放置进去开始的8个数组,我们容易想到,还是取模这个老办法。
运行结果
2.3 取模:& (arr.length - 1)
取模操作是一个常用的例子,翻看各种工程代码,我们会发现,取模,它并不是这样写的。
而是
嗯,一个奇怪的写法,不妨先试试。
注意,我将hashCode % 8
换成了hashCode & (8 - 1)
,再看下运行结果。
嗯,结果还是一样。
通过位运算进行快速取模
2.分析
2.1 原理
通过位运算进行快速取模,使用的前提是数组长度是2的幂次方
。
当数组的长度是2的幂次方时,arr.length - 1
的二进制表示中所有位都是1,例如:
- 数组长度为4时,二进制表示为100,
arr.length - 1
的二进制表示为011
。 - 数组长度为8时,二进制表示为1000,
arr.length - 1
的二进制表示为0111
。 - 数组长度为16时,二进制表示为10000,
arr.length - 1
的二进制表示为01111
。 - 以此类推。
现在,看一下&
操作的原理。位运算&是对元素进行且的操作,即串联
。
有假则假,全真为真。
我们再来看下上面的arr.length - 1
,结果固定是0和n个1。
取模是在干什么,就是找到那个余数。
当取余的时候,不管参与运算的数有多大,(arr.length-1)
的前面都是0。
所以,当参与&运算时,前面的x位数字都可以忽略掉了(结果必定是0),我们实际要关心的只有(arr.length-1)
的这个窗口。
由于(arr.length-1)
后面几位都是111...
,所以结果只会受输入的影响,值是什么结果就是什么即,前面提到的。
拿150举例,相当于把某倍数的arr.length-1
(8)扔掉,即10010(144),扔掉,取出余数6。
2.2 总结
当数组(槽)长度是2n时,元素k的取模操作可替换为位运算以提高效率。
简单抽取一个方法.
3.扩展
3.1 HashMap的取模运算
HashMap 的取模公式为e.hash & (capacity - 1)
。
这里 capacity 是 HashMap 数组结构的大小,约定为 2 的 n 次幂,记为 capacity = 2n。
对于节点 e,它的哈希值用 e.hash 表示。
3.2 HashMap扩容时高低位链表迁移
为啥低位链表要用(e.hash & oldCap) == 0
判断啊?
当链表节点被迁移到新的哈希桶时,核心逻辑是根据哈希值重新分配这些节点。
我们拆成3部分看。
- 部分1:定义两个链表,低位、高位。
- 部分2:填充高低位链表的具体元素
- 部分3:放置高低位链表到哈希桶的具体位置,低位不变还是原来的位置
j
,高位放到新位置j+oldCap
。
首先,咱们为啥要区分高低位这么2个链表出来?
不妨看看我们的填充条件。
我们要有几个基本的知识(还是先建议你去看下我的那篇文章)
-
&是串联:有0则0,全1为1。
-
&(length-1)
实际上就是在比较最后的length-1
,因为length-1
前面的都是0,直接扔掉即可。
然后呢,我们看下面这个,以16到32为例。
我们现在知道旧的16是0001 0000
,也知道if中&的判断结果是0000 0000
。
那么xx的第5位(1那里)是不是必定是0?其他位我们都可以不管,反正这里肯定是0对不?
那我们再和新的32取模试下,32的二进制是0010 0000
,length-1
是0001 1111
。
来,我们做&运算。
哎,关键点来了,后面四个xxxx咱们也不知道是不是1,但是咱们肯定知道结果是这样了。
那既然我们都知道这一位必然为0了,是不是可以再看下15。
悟到了没有,只要能&出0来,实际上就是跟旧的15没区别,它的哈希桶位置不会变。
__EOF__

本文链接:https://www.cnblogs.com/yang37/p/17488009.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具