唯一安全的遍历中删除方式是 iterator.remove(),因其与迭代同步且被 jvm fail-fast 机制允许;其他方式如 for 循环调 list.remove() 会触发 concurrentmodificationexception。

用 Iterator.remove() 是唯一安全的遍历中删除方式
直接在 for 循环里调 list.remove() 会抛 ConcurrentModificationException,不是“偶尔出错”,是 JVM 层面的 fail-fast 机制强制拦截。根本原因:ArrayList 和 LinkedList 的迭代器在检测到结构修改(且非自身 remove() 调用)时立即报错。
正确做法只有这一种:用 Iterator 的 remove() 方法 —— 它是唯一被允许的、与迭代同步的删除入口。
- 必须用
iterator.hasNext()+iterator.next()驱动循环,不能混用for (int i = 0; ...) - 每次调
iterator.remove()前,必须刚执行过一次iterator.next(),否则抛IllegalStateException - 一个
next()对应最多一次remove(),重复调会出错
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("tmp")) {
it.remove(); // ✅ 安全
}
}</string>
removeIf() 更简洁,但 Java 8+ 才有
removeIf() 内部仍用 Iterator 实现,语义清晰、代码短,适合单条件过滤。它不是语法糖,而是标准库提供的安全封装。
注意:它会遍历全部元素,无法提前退出;如果逻辑复杂或需中途 break,还是得回退到手动 Iterator。
- 参数是
Predicate<t></t>,不能抛受检异常,业务逻辑里有 IO 或 try-catch 要包一层 - 对
CopyOnWriteArrayList也有效,但实际很少用——它的写操作开销大,通常不用于频繁删除场景 - 不要和
stream().filter().collect()混用:后者新建集合,原 list 不变;removeIf()是原地修改
list.removeIf(s -> s == null || s.trim().isEmpty()); // ✅ 原 list 被修改
为什么 for (int i = 0; i 反向删能“看起来” work?
反向循环(i--)确实不会触发 ConcurrentModificationException,因为没走迭代器,但结果极易出错:删掉索引 i 元素后,后面所有元素前移,而 i 自减,导致下一个元素被跳过。
这不是“可用方案”,是隐藏 bug 的温床。哪怕测试用例没暴露,上线后数据一变就漏删。
- 正向循环删元素后
i--补位?逻辑绕、易错、可读性差 - 用
list.subList().clear()批量删?只适用于连续区间,不适用条件过滤 - 收集待删元素再批量删?内存多一份引用,小列表无所谓,大列表 GC 压力明显
Stream filter + collect 适合不可变场景
如果原 List 不该被修改(比如是入参、被多处引用),那就别删,该新建。用 stream().filter().collect(Collectors.toList()) 最干净。
性能上:小列表差异忽略;大列表要多一次遍历 + 新建数组 + 复制,比 removeIf() 略慢,但换来的是线程安全(无副作用)和语义明确。
-
Collectors.toCollection(LinkedList::new)可指定返回类型,避免强转 - 如果后续还要流式处理,直接链式调用,不用中间变量
- 别在 filter 里改外部状态(如计数器、set.add),这破坏函数式原则,也容易并发问题
List<string> filtered = list.stream()
.filter(s -> !s.isEmpty())
.collect(Collectors.toList()); // ✅ 新 list,原 list 不动</string>
真正麻烦的从来不是“怎么删”,而是删之前没想清:这个 list 是谁在用?有没有其他线程可能正在读?删完要不要保留原始顺序?这些才是决定用 Iterator.remove()、removeIf() 还是重建的分水岭。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











