Java HashMap 动态扩容在 size > capacity × loadFactor(如16×0.75=12)时触发,扩容时数组翻倍、元素重分布,并利用高位位运算优化重哈希,避免重复计算 hashCode。

Java HashMap 的动态扩容不是“等满了再扩”,而是当元素数量超过 容量 × 负载因子(默认 16 × 0.75 = 12)时立即触发。扩容本质是重建存储骨架:数组翻倍、元素重分布,同时利用位运算避免重复计算哈希值。
扩容触发的关键条件
扩容只在 put() 过程中检查,且满足两个条件:
- size > threshold:当前键值对数量严格大于阈值(注意是“大于”,不是“大于等于”);
-
插入已完成:JDK8 是先完成新元素插入,再判断是否扩容(
++size > threshold),和 JDK7 的“先判断后插入”不同; - 若自定义初始容量(如
new HashMap(7)),实际容量会被自动修正为 ≥7 的最小 2 的幂(即 8),这是通过tableSizeFor()实现的。
扩容时如何不重新计算完整 hash
HashMap 不对每个元素调用 hashCode() 再哈希,而是复用原有 hash 值,靠位运算快速定位新下标。核心逻辑是:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 旧容量为
oldCap(如 16,二进制10000),新容量为oldCap (32,<code>100000); - 关键判断:
e.hash & oldCap—— 若结果为 0,元素留在原位置;若为 1,则新下标 = 原下标 +oldCap; - 例如:旧下标是 5(
00101),oldCap = 16(10000),若hash第 4 位(从 0 开始)是 0,则新下标仍是 5;若是 1,则新下标是 5 + 16 = 21。
链表与红黑树的迁移策略
迁移不是简单遍历复制,而是按结构分类处理:
立即学习“Java免费学习笔记(深入)”;
- 单节点:直接按高位判断,放入新数组对应位置;
-
链表:拆成两个子链表(低位链、高位链),分别挂到新数组的
index和index + oldCap; - 红黑树:先拆成两个链表,若任一链表长度 ≤ 6,则转回链表;否则重构为两棵红黑树。
扩容后的状态变化
一次扩容完成后,内部关键字段同步更新:
- 数组引用
table指向新创建的Node[](长度翻倍); - 容量
capacity变为旧值 × 2; - 阈值
threshold更新为newCapacity × loadFactor(如 32 × 0.75 = 24); - 所有元素已重新分布,哈希冲突概率下降,平均查找性能恢复接近 O(1)。

















