安全删除list元素的唯一正确方式是使用iterator的remove()方法,即先调用next()再调用remove();增强for、普通for循环直接调用list.remove()或foreach/stream都会触发concurrentmodificationexception。

用 for 循环遍历 List 时怎么安全删元素
直接在 for (int i = 0; i 里调用 <code>list.remove(i) 会跳过下一个元素——因为删掉一个后,后面所有元素前移,而 i 还是自增了。比如删索引 2,原索引 3 的元素现在在 2,但循环已跳到 3。
- 要正向删,得把
i减一:list.remove(i--) - 更稳妥的是倒序删:
for (int i = list.size() - 1; i >= 0; i--),删完不影响前面索引 - 如果只是筛选,别边遍历边删,改用
removeIf()或流式过滤
增强 for 循环(for (Type e : list))为什么一删就抛 ConcurrentModificationException
它底层用的是迭代器,但没暴露出来。每次调用 next() 前都会检查 modCount 是否被外部修改过。只要你在循环体里调用 list.remove(),modCount 就变了,下一次 next() 就触发异常。
- 绝对不要在增强
for里调用list.remove()或list.add() - 想删,必须用迭代器自己的
remove()方法,也就是显式写Iterator - 注意:
iterator.remove()只能紧跟在next()后调用一次,否则抛IllegalStateException
Iterator 遍历时删元素的唯一正确写法
这是唯一能在遍历中安全修改集合的方式,前提是用迭代器自己提供的 remove(),而不是集合的 remove()。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
Iterator<string> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s.startsWith("a")) {
it.remove(); // ✅ 正确:调用迭代器的 remove
}
}</string>
- 不能在
it.next()前或连着两次调用it.remove() - 多线程环境下仍不安全,
Iterator本身不是线程安全的 - 对
CopyOnWriteArrayList例外:它的Iterator是快照式的,remove()实际无效(不报错但也不删)
forEach() 和 stream().forEach() 能不能删元素
都不能。两者都基于迭代器实现,且不提供删除入口。list.forEach(e -> list.remove(e)) 会直接抛 ConcurrentModificationException;stream().forEach() 同理,而且流一旦开始消费就不能中途修改源集合。
-
forEach()是Iterable默认方法,本质还是走Iterator - 真要过滤,用
removeIf()(JDK 8+):list.removeIf(s -> s.startsWith("a")) - 或者用流收集新集合:
List<string> filtered = list.stream().filter(s -> !s.startsWith("a")).collect(Collectors.toList())</string>
实际写业务代码时,最常踩的坑不是“不会选”,而是混用场景:比如用增强 for 处理逻辑,顺手加个 list.remove() 调试,上线后偶发崩溃。记住一点:只要涉及修改,就别碰增强 for 和 forEach,老实用 Iterator 或 removeIf。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










