调用 ArrayList.ensureCapacity() 可避免多次扩容导致的内存分配与复制开销。ArrayList 底层为动态数组,扩容时需创建新数组并复制元素,连续添加大量数据会引发“内部震荡”,表现为 CPU 突增、GC 压力上升和响应抖动;预估总数后调用 ensureCapacity(n) 可一次性预留足够空间,不改变 size(),仅提前分配内存,显著提升批量写入性能。

当你已经预知查询结果的总数(比如通过 SQL 的 COUNT(*) 或接口预估),调用 ArrayList.ensureCapacity() 可以避免后续 add() 过程中反复扩容,显著减少数组复制开销和内存震荡。
为什么扩容会造成“内部震荡”
ArrayList 底层是动态数组,初始容量通常为 10。每次添加元素超过当前容量时,会触发扩容:创建新数组(通常是原容量 1.5 倍),再把旧元素逐个复制过去。若连续添加大量元素(如 10000 条记录),可能经历多次扩容(如 10 → 16 → 24 → 36 → 54 → …),产生多次内存分配与复制,即所谓“内部震荡”——表现为 CPU 突增、GC 压力上升、响应时间抖动。
如何精准使用 ensureCapacity()
在开始添加元素前,用预估的总数量调用 ensureCapacity(),确保底层数组一次到位:
- 如果确定最终有
n个元素,直接调用list.ensureCapacity(n) - 不必担心“过度预留”:只是提前分配数组空间,不改变
size(),也不影响遍历或序列化 - 注意不是构造时指定容量(
new ArrayList(n))——那设置的是initialCapacity,而ensureCapacity()是运行时主动扩容保障
典型场景示例
比如分页查询前先查总数,再批量加载数据:
立即学习“Java免费学习笔记(深入)”;
int totalCount = countService.countOrdersByUser(userId); // 如返回 8723
ArrayList<Order> orders = new ArrayList<>(); // 初始容量 10
orders.ensureCapacity(totalCount); // 一次性预留 8723 容量
// 后续循环 add 不再触发扩容
for (Order order : orderMapper.selectByUser(userId)) {
orders.add(order);
}
对比不调用 ensureCapacity(),上述过程可减少约 10 次数组复制(取决于默认增长策略),尤其在高频批量写入时效果明显。
注意事项与边界情况
ensureCapacity() 是“最小保障”,不是精确截断:
- 传入值 ≤ 当前容量时,方法直接返回,无副作用
- 传入值 > 当前容量时,才触发扩容逻辑(按 grow() 规则,至少满足所需容量)
- 它不改变
size(),也不会填充 null 占位;集合仍为空,只是“准备好了足够大的空房间” - 若实际添加数量远小于预估,会浪费少量内存,但远好于频繁扩容的性能损耗


















