Java中ArrayList批量删除元素的高效方法是原地覆盖(双指针),一次遍历、O(n)时间、O(1)空间;其次为倒序遍历remove()、Iterator.remove()或转HashSet后的removeAll()。

在 Java 中,ArrayList 删除大量满足条件的元素时,**正向遍历 + remove() 会严重低效**,因为每次删除都会触发后续元素前移(时间复杂度 O(n)),整体退化为 O(n²)。高效做法是:**倒序遍历 + remove(),或用 removeAll() / Iterator.remove(),最优选则是「原地覆盖」(双指针)**。
倒序遍历 + remove(i),避免元素搬移干扰索引
正向遍历中,删掉一个元素后,后面所有元素索引都减 1,若继续 i++ 就会跳过下一个元素;而倒序遍历(i 从 size-1 到 0)删除不影响前面未处理元素的索引:
for (int i = list.size() - 1; i >= 0; i--) {
if (shouldRemove(list.get(i))) {
list.remove(i); // 安全:只影响 i 后面的索引,已处理完毕
}
}
该方法简单、内存零额外开销,适合中小规模(如几千以内)且删除比例不高的场景。
使用 Iterator.remove(),安全又标准
这是官方推荐的遍历中删除方式,内部调用的是 fast-fail 的 safe remove,不会抛 ConcurrentModificationException:
立即学习“Java免费学习笔记(深入)”;
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("tmp")) {
it.remove(); // 唯一允许的删除方式
}
}
注意:不能用 for-each 循环直接调 remove(),会报错;必须显式用 Iterator。
批量删除:先收集再 removeAll()(适合删除条件简单、目标多)
如果要删的元素容易构造(比如已知所有待删值),可先放进另一个集合,再用 removeAll:
List<String> toRemove = list.stream()
.filter(s -> s.length() > 10)
.collect(Collectors.toList());
list.removeAll(toRemove); // 底层仍是遍历+标记,但对 ArrayList 是优化过的
⚠️ 注意:removeAll() 对 ArrayList 实际执行的是两层循环(外层遍历原 list,内层检查 toRemove.contains),若 toRemove 是 ArrayList,contains 是 O(n),整体 O(n²);**务必把 toRemove 转成 HashSet 提升查找为 O(1)**:
Set<String> toRemoveSet = list.stream()
.filter(s -> s.length() > 10)
.collect(Collectors.toCollection(HashSet::new));
list.removeAll(toRemoveSet);
最高性能:原地覆盖(双指针),一次遍历完成,无扩容/搬移
当需要保留“不满足删除条件”的元素,且不在乎原始顺序(或可接受顺序变化),用双指针法最高效——真正 O(n) 时间、O(1) 额外空间:
int writeIndex = 0;
for (int readIndex = 0; readIndex < list.size(); readIndex++) {
if (!shouldRemove(list.get(readIndex))) {
list.set(writeIndex++, list.get(readIndex));
}
}
// 清理尾部冗余元素
for (int i = list.size() - 1; i >= writeIndex; i--) {
list.remove(i);
}
// 或更简洁:list.subList(writeIndex, list.size()).clear();
该方法完全规避了 remove() 的数组复制开销,特别适合删除比例高(如删掉 70% 以上)的大列表(数万级及以上)。

















