Java HashMap通过两步转换键为数组索引:先用h^(h>>>16)扰动哈希值以混合高低位、减少冲突,再用hash&(length-1)位运算替代取模快速定位桶位置,依赖数组长度为2的幂次方保证高效与均匀分布。

Java HashMap 通过两步完成“键 → 数组索引”的转换:先优化哈希值,再用位运算快速定位桶位置。整个过程不依赖取模(%),而是靠数组长度为 2 的幂次方这一设计,实现高效且均匀的索引计算。
1. 哈希值扰动:混合高低位,减少冲突
直接使用 key.hashCode() 容易导致低位重复、高位未参与运算,尤其在键的哈希码分布集中时(如 Integer 小范围值、字符串前缀相同),会加剧哈希碰撞。HashMap 用以下方式优化:
- 若
key == null,哈希值固定为 0; - 否则,执行
h ^ (h >>> 16),其中h = key.hashCode(); - 右移 16 位再异或,让高 16 位“参与”低 16 位的计算,提升低位的随机性。
2. 索引计算:用位与替代取模,保证效率与范围
假设当前桶数组长度为 table.length(始终是 2 的幂,如 16、32、64…),则索引公式为:
index = hash & (table.length - 1)
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 因为
table.length是 2 的幂,table.length - 1的二进制全是 1(如 16→15→1111); -
&运算等价于对table.length取模,但无需除法,性能更高; - 该操作天然保证结果在
[0, table.length - 1]范围内,不会越界。
3. 实际例子:put("hello", 99) 时发生了什么
以默认初始容量 16 为例:
-
"hello".hashCode()返回 99162322; - 扰动后:
hash = 99162322 ^ (99162322 >>> 16)≈ 得到一个更分散的新整数; -
index = hash & 15(因16 - 1 = 15),只保留低 4 位,结果必为 0~15 中的一个。
4. 为什么不能直接用 hashCode() % length?
取模运算开销大,且当 length 不是 2 的幂时,% 无法保证索引均匀——而 HashMap 强制扩容为 2 的幂,并配合 & (length-1),正是为了兼顾速度与分布质量。

















