hashmap扩容在元素数超阈值(如默认16×0.75=12,第13个put触发)或链表长≥8且数组长度<64时发生;扩容将容量翻倍、新建数组、通过高低位拆分迁移元素,避免全量rehash;该设计以空间换时间,提升迁移效率并保障线程安全下的顺序性。

面试时讲 HashMap 扩容机制,重点不是背源码,而是说清逻辑、抓住关键设计意图。你可以按“触发条件→扩容动作→迁移优化→实际影响”四层展开,既显深度又不堆术语。
什么时候会触发扩容?
两个明确场景:
- 元素总数(size)超过阈值:threshold = capacity × loadFactor,默认是 16 × 0.75 = 12,第13个 put 就会触发
- 链表长度达到8,但数组长度还小于64:此时不转红黑树,而是先扩容——因为扩容后哈希更分散,链表自然变短,比树化更轻量
扩容做了什么?
核心就三步:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 新容量 = 旧容量 × 2(始终维持为 2 的幂次方,这是位运算高效定位的前提)
- 新建 Node[] 数组,大小为新容量
- 把老数组里所有元素,重新分配到新数组中(即 rehash 迁移)
数据是怎么迁移的?JDK 8 的关键优化在哪?
JDK 8 不再像 JDK 7 那样逐个重新计算 hash 和下标,而是用“高低位拆分”——本质是利用容量为 2 的幂这个特性:
- 旧下标 = hash & (oldCap − 1)
- 新下标只有两种可能:要么不变(原位置),要么原位置 + oldCap
- 判断依据:看 hash 的新增高位 bit 是否为 1 → 即 hash & oldCap 是否为 0
- 这样链表可整体拆成两个子链(low 链和 high 链),直接挂到新数组对应位置,避免反复插入和 rehash 计算
迁移对性能和行为有什么影响?
要提一点实战意识:
- 扩容是同步阻塞操作,期间所有 put/get 都要等,所以高并发写场景下,预估 size 初始化容量很关键
- 链表迁移保持原有顺序(尾插法),不会像 JDK 7 那样导致环形链表和死循环
- 红黑树也会按高低位拆成两棵,节点数 ≤ 6 的会退化回链表——说明树化不是终点,而是动态平衡的一部分
讲完可以补一句:“本质上,扩容不是简单地‘变大’,而是通过翻倍+位运算,把一次全局重散列,变成局部二分拆解,这是空间换时间的典型工程权衡。”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










