扰动函数通过h ^ (h >>> 16)将高位信息混入低位,缓解因数组长度为2的幂导致仅低k位参与索引计算而引发的低位重复冲突;它不消除所有冲突,但显著提升桶分布均匀性。

HashMap 用扰动函数((h = key.hashCode()) ^ (h >>> 16))来让哈希值的高位参与索引计算,从而缓解因低位重复导致的桶分布不均和哈希冲突。
为什么只用 hashCode 低位容易冲突
HashMap 数组长度总是 2 的幂(如 16、32、64),计算下标用的是位运算:i = (n - 1) & hash。这等价于对 n 取模,但只保留了 hash 的低几位 —— 比如 n = 16 时,n-1 = 15(二进制 1111),实际只取 hash 的低 4 位。
很多对象的 hashCode() 在低 16 位上高度相似(比如连续 ID、简单字符串、小整数),而高位差异大。若直接用原始 hashCode,这些相似的低几位就会全部映射到少数几个桶里,冲突激增。
扰动函数怎么起作用
它把 hashCode 的高 16 位右移后与低 16 位异或,让高位信息“混入”低位:
OpenJDK 24 Windows x64 官方参考实现(24+36),适合 Windows 环境进行 Java 开发、编译、运行和版本兼容性验证。
- 异或操作是可逆且均匀的:相同位异或得 0,不同位得 1,能放大微小差异
- 高位被“折叠”进低位:即使原始 hash 低 4 位全为 0,扰动后可能变成非零值
- 结果仍保持散列性:不会破坏原有分布,只是让低位更“有信息量”
例如:hashCode = 0x0000abcd(高位全 0,低位 abcd)→ 扰动后变成 0x0000abcd ^ 0x00000000 = 0x0000abcd;而 hashCode = 0x1234abcd → 扰动后是 0x1234abcd ^ 0x00001234 = 0x1234bed9,低 4 位已改变。
扰动后如何影响桶分布
最终下标由 (n - 1) & hash 决定。扰动让原本因低 4 位相同而挤在同一个桶里的 key,现在低 4 位更分散,从而落到不同桶中:
- 没扰动前:100 个 key 的
hashCode低 4 位都是1010→ 全进下标 10 - 加扰动后:它们的扰动值低 4 位可能变成
0011、1101、0100… → 分散到多个桶
这不是消除冲突,而是显著降低**相同低 k 位导致批量冲突**的概率,尤其对常见业务 key(如递增 ID、短字符串)效果明显。
它不是万能的,但很务实
扰动函数不能解决所有冲突 —— 两个不同 key 经过扰动后仍可能得到相同 hash 值,这是哈希本质决定的。但它针对 HashMap 实际使用中最常见的冲突诱因(低位趋同)做了低成本、高收益的优化:
- 计算极快:一次右移 + 一次异或,无分支、无内存访问
- 兼容性强:不依赖 key 类型,对任意
hashCode都有效 - 配合链表/红黑树兜底:即使还有冲突,也能通过结构升级控制最坏复杂度
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










