HashMap扩容在size超过threshold(capacity×loadFactor,默认12)时触发,首次put也初始化数组;新容量翻倍且必为2的幂以支持高效位运算定位;迁移采用高位判断法,根据hash新增位0或1决定新下标为原下标或原下标加旧容量,避免全量rehash。

HashMap 底层数组扩容不是简单地“换更大数组”,而是一套兼顾效率、分布均匀性和线程安全(单线程下)的完整重散列过程。核心在于:容量翻倍 + 位运算快速重定位 + 避免全量 rehash。
扩容触发时机
扩容发生在 put 操作后,当当前元素个数 size 超过阈值 threshold 时触发:
-
threshold = capacity × loadFactor,默认为16 × 0.75 = 12 - 插入第 13 个元素时,
size变为 13,大于 12,立即调用resize() - 注意:首次
put也会触发resize,用于初始化底层数组(懒加载)
新数组创建规则
新容量严格为旧容量的 2 倍,且始终维持为 2 的幂:
- 16 → 32 → 64 → 128 …… 最大不超过
2^30 - 必须是 2 的幂,才能使用
hash & (capacity - 1)替代取模,保证下标计算高效且分布均匀 - 若构造时传入非 2 的幂初始容量(如 10),HashMap 会自动向上找最近的 2 的幂(即 16)
老数据迁移的关键优化:高位判断法
JDK 8 最关键的改进,就是迁移时不重新计算每个 key 的完整 hash 值,而是利用新旧容量的二进制关系做快速分流:
立即学习“Java免费学习笔记(深入)”;
- 旧容量为
n(如 16,二进制10000),新容量为2n(如 32,二进制100000) - 新旧容量差为
n,对应二进制中唯一多出的那一位(第log₂(n)位) - 对任一节点,只需检查其 hash 值在该“新增位”上是 0 还是 1:
- 若为 0 → 新下标 = 旧下标
- 若为 1 → 新下标 = 旧下标 +
n
- 举例:旧下标为 5(
hash & 15= 5),新容量 32,则新下标为5或5 + 16 = 21,取决于 hash 的第 4 位(从 0 开始)
链表与红黑树的分别处理
迁移过程中,不同结构按规则拆分:
- 普通链表:遍历原链表,按高位结果分别头插或尾插到两个新位置(JDK 8 使用尾插法,避免 JDK 7 的死循环问题)
- 红黑树节点:先拆成两条链表,若拆分后某条链长度 ≥ 8 且新数组容量 ≥ 64,再转回红黑树;否则退化为链表
- 空桶直接跳过,不参与任何计算


















