商城首页欢迎来到中国正版软件门户

您的位置:首页 >如何在 Java 中使用 ArrayList.ensureCapacity() 减少由于频繁增删导致的数组重分配

如何在 Java 中使用 ArrayList.ensureCapacity() 减少由于频繁增删导致的数组重分配

  发布于2026-05-01 阅读(0)

扫一扫,手机访问

如何在 Ja va 中使用 ArrayList.ensureCapacity() 减少由于频繁增删导致的数组重分配

如何在 Ja va 中使用 ArrayList.ensureCapacity() 减少由于频繁增删导致的数组重分配

ensureCapacity() 真的能减少重分配吗?

答案是肯定的,但这里有个关键前提:它只对“新增”操作有效,而且必须在执行大量 add() 之前就调用。至于 remove() 操作,这个方法完全帮不上忙,它也不会主动回收已经分配出去的空间。道理很简单,ArrayList 内部的数组扩容,只在 add() 发现容量不够时才会触发。而 ensureCapacity() 的作用,就是提前把底层的 elementData 数组扩大到指定尺寸,从而避免后续多次的 resize 操作——要知道,每次 resize 都意味着分配新数组和拷贝全部元素,这可是 O(n) 级别的开销。

这里有个常见的误解:有人以为调用一次 ensureCapacity(100),之后即使删除了90个元素,内存也会自动缩回来。其实不然。ArrayList 的设计决定了它从不自动缩小底层数组,想释放冗余内存,得靠另一个方法 trimToSize()。而且,这也不属于“减少重分配”的范畴,而是纯粹的内存回收行为。

什么时候调用 ensureCapacity() 才真正省开销?

最典型的场景,就是那种“可预估最终规模的批量构建”。比如,从数据库里查询出5000条记录逐条添加,或者解析一个已知长度的JSON数组。在这种情况下,于循环开始前调用 ensureCapacity(),就能完美避免多次扩容。想想看,默认初始容量只有10,按1.5倍的策略扩容,要装下5000个元素,中间至少得经历大约12次数组拷贝,这个开销可不小。

  • ✅ 推荐场景:已知最终的元素数量(比如在 list.size() == expectedSize 成立之前),并且主要操作是 add()
  • ❌ 无效场景:一边添加一边删除、或者进行随机位置的插入(add(int index, E) 虽然可能引发数组元素移动,但不会触发扩容逻辑)。
  • ⚠️ 特别注意ensureCapacity() 的参数是“最小需要容量”,而非“精确容量”。传入100,只保证数组长度不小于100;如果当前容量已经大于等于100,那这个方法就什么都不做。

和构造函数 initialCapacity 比,有啥区别?

从本质上讲,两者目的相同,都是设置初始数组大小。区别在于时机:new ArrayList(100) 在对象实例化时就分配好了数组;而 ensureCapacity(100) 则是对已经存在的实例进行“事后补救”。两者的实际性能差异微乎其微,但使用时机不同,能让代码语义更清晰:

  • 如果在构造时就已经清楚知道数据规模,直接用 new ArrayList(initialCapacity)
  • 如果实例已经存在,稍后才确定要装入大量数据,那就用 ensureCapacity()
  • 需要明确的是,无论哪种方式,都只影响底层数组的物理容量,而不改变 size() 返回的逻辑元素个数。调用之后,size() 依然是0。

来看一个具体例子:

ArrayList list = new ArrayList<>();
list.ensureCapacity(5000); // 底层数组现在至少长 5000
for (int i = 0; i < 5000; i++) {
    list.add("item" + i); // 全程无 resize
}

容易被忽略的坑:ensureCapacity() 不解决并发和结构性修改问题

即使你精准地调用了 ensureCapacity(),下面这些情况依然可能引发意外的性能开销甚至异常:

  • 多线程同时 add():即使容量充足,非线程安全的 ArrayList 也可能因竞争导致重复扩容。
  • 使用迭代器或条件删除:通过 list.iterator().remove()removeIf() 删除大量元素后,底层数组容量不变,但后续的 add() 仍会基于当前的 size() 判断扩容,可能造成严重的空间浪费。
  • 清空后复用:调用 clear() 后继续添加,虽然底层数组没变,但 size 已归零。如果之后只加几十个元素,那么之前预分配的5000容量就成了闲置内存。

所以说,预分配容量只是一种优化手段,不能替代对实际使用模式的判断。在真正高频增删的场景下,或许该考虑换成 LinkedList(插入删除快)、或者使用 ArrayDeque(作为栈或队列更高效),甚至评估对象池方案——而不是仅仅盯着 ensureCapacity() 这一个方法。

本文转载于:https://www.php.cn/faq/2399804.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注