往hashmap存20个元素,会扩容几次?
扩容次数不能只看元素个数,必须同时说明初始容量、负载因子、构造方式以及首次分配发生的时点。
直接结论
没有给出初始容量与负载因子时,答案不是一个固定数字。以 Java 21 的常见默认配置解释:默认负载因子是 0.75,首次真正插入时才分配默认容量 16 的表,阈值为 12;连续放入 20 个互不覆盖的新键时,第 13 个元素触发容量从 16 扩到 32。因此若把首次建表视为初始化而不是扩容,扩容 1 次;若面试官把从未分配到 16 也计入,则会说发生 1 次初始化加 1 次扩容。应先声明口径。
原理与步骤
HashMap 用容量与负载因子的乘积形成扩容阈值。每次 put 先计算哈希并定位桶;新键实际增加 size 后,如果 size 超过 threshold,就扩大表并重新分布桶。默认构造器并不会立即创建 16 个桶,所以仅从构造代码看不到数组。计算过程应写清:插入 1 到 12 个新键不超过阈值;第 13 个使 size 变为 13,触发 16 到 32;新阈值约为 24;第 20 个仍未超过 24。相同键更新 value 不增加 size,也不会按这个计数触发扩容。
工程场景
工程上应根据可预估元素数预设容量,避免批量装载时连续迁移。例如预计保存 20 个唯一键,可按目标元素数和负载因子反推所需容量,并注意 HashMap 会选择满足要求的二次幂容量。面试回答可以补充:树化、链表长度和桶容量是碰撞处理问题,不等于每次树化都会扩容;高碰撞键、糟糕的 hashCode 或动态增长都会改变运行成本。并发写场景也不应靠调大容量解决,应选择并发容器或外部同步。
验证与边界
验证时用同一 JDK 版本构造默认 HashMap,插入 20 个唯一且哈希分布正常的键,观察 size 与延迟变化;若必须确认内部数组,只能在受控测试中借助诊断工具,业务代码不应依赖私有字段。边界包括指定初始容量、不同负载因子、重复键、删除后再插入、极端碰撞以及 JDK 实现变化。官方 API 只承诺容量和负载因子的性能关系,不承诺所有内部迁移细节,因此回答要区分公共契约与实现观察。
答题练习
- 1先声明扩容口径
- 2默认阈值 16×0.75=12
- 3第 13 个唯一键触发 16 到 32
常见错误
- 把首次延迟分配和扩容混为一谈
- 忽略重复键不会增加 size
可能追问
- 如何为预计元素数计算初始容量
- 碰撞、树化和扩容分别解决什么问题
来源记录
- 原始来源
- 小林coding
- 来源页面
- Java集合面试题
- 最近收录
- 2026-07-04
- 官方复核
- Oracle Java 官方文档