ArrayList频繁插入性能下降主因是扩容引发的数组复制;应预设容量、避免中间插入、优先批量添加,并根据实际需求选用LinkedList或ArrayDeque等替代结构。

频繁插入 ArrayList 时性能下降,核心问题不在“插入”本身,而在于扩容引发的数组复制和内存重分配。只要避开中间插入、控制扩容次数,优化效果立竿见影。
优先预设容量,从源头减少扩容
ArrayList 默认初始容量为 10,每次扩容按 1.5 倍增长(不是翻倍)。插入 10 万个元素时,可能触发约 20–30 次扩容,每次都要复制全部已有元素——这是主要开销来源。
- 已知数据规模:直接用带参构造函数,如 new ArrayList(100000)
- 不确定精确数量但可估算:调用 list.ensureCapacity(expectedMin),比如预计至少 8 万,就传 80000
- 批量插入前统一调用一次,比循环中反复判断更高效
避免在头部或中间插入
ArrayList 是数组实现,非尾部插入=移动后续所有元素。哪怕只插在索引 0,也要把全部现存元素往后挪一位,时间复杂度 O(n)。
- 尾部插入(add(e))才是真正的 O(1) 分摊成本
- 若业务逻辑必须头插,考虑改用 LinkedList(但注意它随机访问慢、内存占用高)
- 若需维持顺序又常插中间,可先用 ArrayList 收集,最后一次性排序或用其他结构替代
配合使用 addAll 而非逐个 add
即使容量足够,连续调用 10000 次 add() 仍涉及重复边界检查、size 自增等微小开销。而 addAll() 可批量处理,内部做一次范围校验 + 一次系统级数组拷贝(System.arraycopy),效率更高。
立即学习“Java免费学习笔记(深入)”;
- 适合从另一个集合、数组或流中批量导入数据
- 例如:list.addAll(Arrays.asList(dataArray))
- 注意:addAll 不会自动扩容,务必确保目标 list 已有足够容量
必要时换数据结构
如果插入只是手段,核心需求是“高频增删+不强调随机访问”,那 ArrayList 就不是最优解:
- LinkedList:插入/删除任意位置都是 O(1),但访问 get(i) 是 O(n),且每个元素多两个指针内存开销
- ArrayDeque:适合队列/栈场景,头尾操作极快,内存紧凑,但不支持按索引随机修改
- 若插入后主要做查找,可考虑 HashSet 或 HashMap(无序、去重、O(1) 查找)



















