jdk 8 hashmap扩容采用高低位掩码法:不重算hash、只判高位比特、分两路搬运;要求容量为2的幂以支持位运算索引拆分,通过hash & oldcap判断高位,0则留原位、非0则移至原索引+oldcap位置。

Java HashMap 在 JDK 8 中通过“高低位掩码”实现扩容数据迁移,核心在于**不重算 hash、只看高位比特、分两路搬运**——这使迁移成本减半,且彻底规避了 JDK 7 的死循环风险。
为什么必须用 2 的幂容量?
这是高低位拆分的前提。当容量 cap = 2ⁿ 时:
- 旧掩码为 cap − 1 = 0b111…1(n 个 1),索引计算用
hash & (cap − 1),等价于取低 n 位; - 新容量为 2 × cap = 2ⁿ⁺¹,新掩码为 (2 × cap) − 1 = 0b111…1(n+1 个 1);
- 新掩码比旧掩码多出一位——第 n 位(从 0 开始计),这一位正是判断“去哪”的关键。
高位判断:一个位运算决定去留
对每个节点的 hash 值,执行 hash & oldCap(注意:不是 & (oldCap − 1)):
- 结果为 0 → 第 n 位是 0 → 新索引 = 旧索引(留在原位置);
- 结果非 0 → 第 n 位是 1 → 新索引 = 旧索引 + oldCap(挪到高位桶)。
例如:oldCap = 16(0b10000),看 hash 的第 4 位(即 16 对应的那一位):
• hash = 0x3A(0b00111010),0x3A & 0x10 == 0x10 ≠ 0 → 新位置 = 原位置 + 16;
• hash = 0x2F(0b00101111),0x2F & 0x10 == 0 → 新位置 = 原位置。
高低链表:尾插法保证顺序与安全
遍历旧桶中链表时,不直接移动节点,而是构建两条独立链表:
-
低位链表(loHead/loTail):所有
hash & oldCap == 0的节点,最终放入newTable[i]; -
高位链表(hiHead/hiTail):其余节点,放入
newTable[i + oldCap]; - 全程尾插,节点相对顺序完全保留,避免头插法导致的链表倒置和多线程环形链表。
红黑树也按同样逻辑拆分
TreeNode 同样参与高低位判断:先按普通节点方式拆成两条链表,再分别检查长度:
- 若某条链表节点数 ≤ 6,则退化为普通 Node 链表;
- 若 > 6 且新容量 ≥ 64,才重新构造为 TreeNode 并树化。
整个过程无需调用 key.hashCode() 或扰动函数,也不依赖 hash % newCap,纯粹靠位运算定位,高效且确定。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











