HashMap扩容时复用原hash值,利用oldCap位运算判断迁移位置:若e.hash & oldCap为0则索引不变,否则新索引为原索引加oldCap。

Java HashMap 扩容时并不对每个元素重新计算完整哈希值,而是利用位运算高效推导新索引位置。核心在于:扩容后容量翻倍(如从 16→32),数组长度始终是 2 的幂,因此索引计算公式 index = hash & (length - 1) 中的掩码只多出一位。这使得重定位只需检查哈希值中新增的那一位即可。
为什么不用重新调用 hash() 或 % 运算
HashMap 在 put 阶段已通过扰动函数(h ^ (h >>> 16))完成一次高质量 hash 计算,该值全程复用。扩容时无需再次扰动或取模——因为取模 hash % newCapacity 在 newCapacity 是 2 的幂时,等价于位与 hash & (newCapacity - 1),而后者更快且可被优化为单步判断。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
三种桶类型的具体重定位方式
-
单节点(无冲突):直接执行
e.hash & (newCap - 1)得到新索引,放入新数组对应位置 -
链表(长度 ≤ 7):遍历链表,对每个节点 e 判断
(e.hash & oldCap) == 0:
✓ 结果为 true → 留在原索引位置 i
✗ 结果为 false → 移至新索引位置 i + oldCap -
红黑树(树化后):调用
split()方法,逻辑同链表——依据e.hash & oldCap将节点分拆为两棵子树,分别挂载到原位置和 i + oldCap 位置
关键位运算原理:只看“新增的那一位”
假设 oldCap = 16(二进制 0001 0000),则 newCap = 32(0010 0000)。此时:
• 旧掩码 = 15 = 0000 1111(取低 4 位)
• 新掩码 = 31 = 0001 1111(取低 5 位)
多出来的第 5 位(即 oldCap 对应的那一位),正是决定是否迁移的关键:
→ 若 hash & oldCap == 0,说明该位为 0,新索引 = 旧索引
→ 若 hash & oldCap != 0,说明该位为 1,新索引 = 旧索引 + oldCap

















