ensurecapacity 不是扩容操作,而是预检查并按需分配更大底层数组空间;它不改变 size(),仅确保后续写入不频繁触发 o(n) 扩容复制。

为什么 ensureCapacity 不能“手动触发扩容”
它根本不是扩容操作,只是预检查并提前分配底层数组空间——如果当前容量不足,就新建一个更大的数组,把旧元素复制过去;如果够用,什么也不做。很多同学以为调用它就能“强制扩容”,结果发现 size() 没变、capacity() 也没立刻增长(比如在 ArrayList 中),其实是误读了语义。
真正影响写入性能的,是频繁扩容引发的多次数组复制。一次 add() 可能触发扩容,而扩容成本是 O(n),10 万次追加若触发 15 次扩容,光复制就可能多跑几百万次元素移动。
-
ensureCapacity必须在写入前调用,且传入的是「最小所需容量」,不是“想扩多少就扩多少” - 对空
ArrayList,默认初始容量是 10;若你知道要塞 20000 个元素,直接new ArrayList(20000)比反复ensureCapacity更干净 - 调用后不会清空或重置已有数据,但也不会改变
size()—— 它只管底层数组,不管逻辑长度
ensureCapacity 的正确调用时机和参数计算
适用于你明确知道最终规模,且写入是集中、分批、非流式的场景。例如:解析一个已知行数的 CSV 文件,或批量处理数据库查出的 List<record></record>。
错误做法是边 add 边 check、每 1000 次调一次 ensureCapacity;正确做法是在循环前一次性算好上限:
int expectedSize = 15000; List<string> list = new ArrayList(); list.ensureCapacity(expectedSize); // ← 这里才起作用 for (int i = 0; i <ul> <li>参数应 ≥ 预期最大 <code>size()</code>,不要减去当前 <code>size()</code>(<code>ensureCapacity</code> 接收的是总容量下限)</li> <li>如果数据源本身有 <code>size()</code> 方法(如 <code>Collection</code>、数组),直接用它;否则宁可略高估,也不要低估</li> <li>对 <code>StringBuilder</code> 同理:<code>ensureCapacity(1024 * 1024)</code> 可避免字符串拼接时反复 realloc,但前提是确定最终字符数</li> </ul> <h3>不同容器的 <code>ensureCapacity</code> 行为差异</h3> <p>不是所有集合都有这个方法。只有底层基于数组、且扩容代价显著的类才提供它,典型就是 <code>ArrayList</code> 和 <code>StringBuilder</code>。而 <code>LinkedList</code>、<code>ConcurrentLinkedQueue</code> 或 <code>HashMap</code> 都没有——它们的扩容机制完全不同,或根本不需要连续内存。</p> <ul> <li> <code>ArrayList.ensureCapacity(int)</code>:调整内部 <code>Object[]</code> 数组,策略是“至少达到参数值”,实际分配可能更大(如 JDK 17 中会按 1.5 倍增长规则向上取整)</li> <li> <code>StringBuilder.ensureCapacity(int)</code>:调整内部 <code>char[]</code>,行为类似,但注意它还受 <code>toString()</code> 缓存影响,扩容后旧缓存失效</li> <li> <code>Vector</code> 也有该方法,但它自带同步开销,大规模写入时通常不如 <code>ArrayList</code> + 预分配高效</li> </ul> <h3>容易被忽略的边界和副作用</h3> <p>最常踩的坑,是以为调用 <code>ensureCapacity</code> 就万事大吉,结果发现 GC 压力反而上升,或者并发写入时出现诡异问题。</p> <ul> <li>过度预分配浪费内存:申请 100 万容量却只写 10 万个对象,堆里多占几 MB 无用数组,GC pause 时间可能明显增加</li> <li>多线程下不安全:即使你提前 <code>ensureCapacity</code>,多个线程同时 <code>add()</code> 仍需同步,该方法本身不提供线程保护</li> <li>某些框架封装后屏蔽了该方法:比如 MyBatis 的 <code>ResultHandler</code> 回调中返回的 <code>List</code> 可能是不可变或代理对象,调用 <code>ensureCapacity</code> 会抛 <code>UnsupportedOperationException</code> </li> </ul> <p>真要压榨写入性能,优先级应该是:确认规模 → 构造时指定初始容量 → 避免中间截断/清空/转数组等隐式扩容操作 → 最后才考虑 <code>ensureCapacity</code> 补漏。它是个保险栓,不是加速器。</p></string>










