数组删除性能瓶颈在于元素移动,中间删除最伤性能,因需移动size−index−1个元素;按值删除更慢,涉及查找与移动双重开销;遍历时直接remove会导致漏删、o(n²)复杂度及并发修改异常;有效优化是倒序批量删除。

数组删除操作的性能瓶颈,几乎都卡在“元素移动”这一步——不是删得慢,而是删完之后,后面一大片数据得集体挪位置。这个动作看似简单,实则开销巨大,尤其在中间或开头删时,移动量直接和剩余元素数量挂钩。
为什么中间删除最伤性能
数组底层是连续内存块,不支持“挖空”。调用 remove(int index) 时,JVM 会执行 System.arraycopy,把 index + 1 到末尾的所有元素整体前移一位。移动数量是 size − index − 1:
- 删索引 0(第一个):移动 size − 1 个元素,开销最大
- 删索引 size−1(最后一个):移动 0 个,实际只做 length 减 1,几乎无耗
- 删索引 500(十万元素中):要搬约 99,500 个引用,接近全量搬运
按值删除比按索引删除更慢
remove(Object o) 表面简洁,背后有两重开销:
- 先线性扫描找匹配项:从头开始逐个调 equals(),平均查一半才命中
- 找到后再触发一次前移操作,和 remove(int index) 完全相同
- 若目标在开头,查找快但移动多;若在末尾,查找慢但移动少——总时间仍远高于直接索引删除
遍历时边删边走,错上加错
在 for 循环里写 list.remove(obj),会同时触发三重恶化:
- 每次删除后,后续元素下标全部减 1,导致下一轮循环跳过紧邻的下一个元素(漏删)
- 外层每走一步,内层 remove(Object o) 都重新从头扫描,时间复杂度退化为 O(n²)
- 迭代器检测到 modCount 变更,直接抛 ConcurrentModificationException
真正有效的优化手段
ArrayList 没有原生批量删除接口,但可以绕过重复移动:
- 需删多个时,先用 LinkedHashSet 或 ArrayList 收集所有待删索引(保持顺序或去重)
- 再倒序遍历该索引列表,逐个调 remove(int index)
- 倒序确保前面的删除不影响后面索引的有效性,也使总移动量趋近理论最小值










