扩容影响性能是因为需创建新数组、复制元素(O(n))、增加GC压力;如初始容量为10存1000元素,约扩容9次,复制远超1000次。

ArrayList 在添加元素时,如果当前容量不足,会触发扩容操作,而扩容涉及数组复制,开销较大。提前指定合适的初始容量,能显著减少扩容次数,提升性能,尤其在数据量可预估的场景下效果明显。
为什么扩容会影响性能
ArrayList 底层是 Object[] 数组,当 add() 导致 size ≥ capacity 时,会创建一个新数组(通常是原容量的 1.5 倍),再将旧数组元素逐个复制过去。这个过程时间复杂度为 O(n),且频繁触发还会增加 GC 压力。
- 每次扩容都要分配新内存、拷贝数据、丢弃旧数组
- 若初始容量设为默认 10,而实际要存 1000 个元素,大约需扩容 9 次(10→15→22→33→49→73→109→163→244→366→549→823→1234)
- 累计复制元素次数远超 1000 次,造成明显冗余
如何合理设置初始容量
关键不是“越大越好”,而是尽量贴近最终 size,避免过度浪费内存或频繁扩容。推荐按以下方式估算:
- 明确知道元素总数:直接传入该数值,如 new ArrayList<String>(500)
- 有大致范围(如 300–600):取上限或略高一点,比如设为 650
- 基于业务逻辑推算:例如分页查询每页 20 条,预计最多查 10 页 → 初始容量设为 200
- 不确定但偏多:可结合经验值,如已知通常 >1000,就设 1024(2 的幂便于 JVM 内存对齐,非强制但有益)
注意构造函数的细节
ArrayList 提供了两个常用构造方法,别用错:
- ArrayList(int initialCapacity):正确——指定底层数组初始长度
- ArrayList(Collection c):它会根据 c.size() 设置容量,但内部仍会调用 ensureCapacityInternal,部分版本可能多一次判断;若 c 是空集合,容量仍为 0,首次 add 就触发扩容
- 不要误用无参构造后立即 trimToSize()——这无法避免前期扩容,只是事后收缩,没解决根本问题
配合其他操作进一步优化
单设初始容量还不够,搭配使用可更高效:
- 批量添加优先用 addAll() 而非循环 add(),它会一次性检查总容量并扩容到位
- 确认不再增删时,调用 trimToSize() 释放多余内存(适合长生命周期对象)
- 若元素类型固定且数量极大,考虑是否真的需要 ArrayList——比如纯数字可用 int[] 或 Eclipse Collections 等专用结构


















