Java遍历时安全删除集合元素必须用Iterator.remove(),且仅限next()后、下次next()前执行;直接调集合remove()会触发fail-fast机制抛ConcurrentModificationException。

Java 中遍历时安全删除集合元素,核心就一条:必须用 Iterator.remove(),且只能在 调用 next() 之后、下一次 next() 之前执行。其他任何方式——包括 for-each 里调 list.remove()、普通 for 循环中删索引、甚至连续两次调 it.remove()——都会出错。
为什么不能直接用集合的 remove()?
因为 ArrayList、LinkedList、HashSet 等集合采用 fail-fast 机制:内部维护一个 modCount 计数器,迭代器会记录初始值 expectedModCount。一旦你绕过迭代器,直接调 list.remove() 或 set.remove(),modCount 就变了,而迭代器毫不知情。下次调 hasNext() 或 next() 时,两者一比对不一致,立刻抛 ConcurrentModificationException。
这不是多线程问题,单线程也会崩。比如:
-
错误:
for (String s : list) { if (s.isEmpty()) list.remove(s); }→ 运行即崩 -
错误:
for (int i = 0; i → 可能漏删(删掉索引 1 后,原索引 2 的元素移到 1,但 i 已变成 2)
Iterator.remove() 的正确写法
它不是“能用就行”,而是有明确节奏要求:
立即学习“Java免费学习笔记(深入)”;
- 先获取迭代器:
Iterator<String> it = list.iterator(); - 用
while(it.hasNext())控制循环 - 每次循环内必须先调
it.next(),拿到当前元素 - 只在刚调完 next() 后、还没再次 next() 前,才能调
it.remove()
示例:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
if (s == null || s.trim().isEmpty()) {
it.remove(); // ✅ 安全:紧跟 next(),且只调一次
}
}
更简洁的替代:removeIf()(Java 8+)
如果只是按条件批量删除,Collection.removeIf() 是推荐首选:
- 语义清晰:
list.removeIf(s -> s == null || s.trim().isEmpty()); - 底层就是封装了安全的 Iterator.remove(),不会抛 ConcurrentModificationException
- 支持 List、Set,但不适用于 Collections.unmodifiableList()(会抛 UnsupportedOperationException)
- 注意:不能在 Predicate 里再操作原集合(比如 lambda 里调 list.remove()),行为未定义
复杂逻辑或特殊场景怎么办?
当需要记录被删元素、做日志、或删除逻辑依赖上下文(比如“删前两个匹配项”),removeIf() 不够用,这时可:
- 用 Iterator + 手动计数/状态变量(如
int removed = 0; while (...) { if (...) { it.remove(); removed++; } }) - 两阶段策略:先遍历收集待删元素到临时集合,再统一调
list.removeAll(toRemove);适合逻辑极复杂、且数据量不大的情况 - 多线程环境必须换容器:读多写少用
CopyOnWriteArrayList,高并发用ConcurrentHashMap;别指望加锁 Iterator,它本身不是线程安全方案


















