预设初始容量是优化arraylist性能最有效方式,可避免扩容导致的o(n)复制和gc压力;已知规模用new arraylist(n),估算规模留5%~10%余量,批量添加前可用ensurecapacity()预分配;但仅优化尾部添加,中间插入/删除仍应换用linkedlist等结构。

在大数据量下优化 ArrayList 性能,核心是减少甚至消除动态扩容带来的数组复制开销。扩容本质是创建新数组 + 全量拷贝旧数据,单次就是 O(n),频繁发生会明显拖慢插入速度、增加 GC 压力。
预设足够初始容量
最直接有效的方式:在创建时就告诉 ArrayList “我大概要存多少个元素”。JDK 默认无参构造容量为 10,添加第 11 个元素就会首次扩容(→15),之后按 1.5 倍持续增长,10 万条数据可能触发 17 次扩容。
- 已知确切规模:用 new ArrayList(expectedSize),例如
new ArrayList(100000) - 规模可估算:取略大于预估值的整数,留 5%~10% 余量即可,避免过度浪费内存
- 注意:
size()返回的是当前元素个数,capacity(即elementData.length)才是真实分配的数组长度,二者不同
提前调用 ensureCapacity
适用于无法在构造时确定容量,但能在批量添加前预估的情形。它和构造函数效果一致,都是提前分配底层数组空间,不触发实际扩容逻辑。
- 调用时机:在循环
add()前一次性执行,例如list.ensureCapacity(100000) - 它只在当前容量不足时才真正扩容;若已满足,则无操作,开销极小
- 比反复判断再扩容更安全,也比依赖默认策略更可控
避开低效操作模式
容量优化只解决“尾部添加”的性能问题。若业务涉及大量中间插入/删除,ArrayList 本身就不合适。
- 避免在循环中用
list.add(index, e)或list.remove(index)—— 每次都导致后续元素整体位移,O(n) 开销叠加 - 随机访问(
get(i))和尾部添加(add(e))仍是 O(1),应优先使用 - 批量添加多个元素时,用
addAll(collection)而非循环调用add(),内部会做容量预判和批量复制
配合监控与合理取舍
优化不是越激进越好,需结合场景权衡。
- 小数据量(如几百以内):无需预设,代码简洁更重要
- 高频写入且规模稳定(如日志缓冲、批量导入):强烈建议预设容量,实测可提速 40%~60%,扩容次数归零
- 不确定规模又不能错估太多:可先设一个保守值,再结合
size()和 JVM 监控(如 GC 频率、堆内存变化)反向调整
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











