遍历List时不能直接调用list.remove(),因快速失败机制下modCount不一致会抛ConcurrentModificationException;安全做法是用Iterator.remove()、removeIf()或倒序for循环。

为什么不能在遍历List时直接调用remove()
直接用for-each或Iterator.next()遍历时调用list.remove(obj)会抛ConcurrentModificationException。这是因为ArrayList和LinkedList的迭代器是“快速失败”(fail-fast)的,内部维护modCount计数器,而list.remove()会修改该值,但迭代器没同步感知。
常见错误写法:
for (String s : list) {
if (s.startsWith("a")) {
list.remove(s); // ❌ 抛异常
}
}
- 想边遍历边删元素,必须用
Iterator.remove(),它是唯一被允许的删除方式 -
removeIf()是Java 8+推荐替代方案,内部已做安全封装 - 用普通
for倒序遍历(i = size-1; i >= 0; i--)也可行,但逻辑易错,不推荐用于复杂条件
用Iterator.remove()安全删除符合条件的元素
这是最通用、兼容所有List实现的安全方式,适用于Java 5+。
关键点:
立即学习“Java免费学习笔记(深入)”;
- 必须调用
iterator.remove(),不是list.remove() - 每次只能在
next()之后调用一次remove(),否则抛IllegalStateException - 不支持并发修改——同一
List对象不能在另一个线程里同时增删
示例:
Iteratorit = list.iterator(); while (it.hasNext()) { String s = it.next(); if (s.length() == 0 || s.equals("null")) { it.remove(); // ✅ 安全 } }
Java 8+优先用removeIf()代替手写循环
removeIf(Predicate)语义清晰、代码简洁,且底层自动使用Iterator.remove(),无需手动管理迭代器。
注意:
- 仅适用于
ArrayList、LinkedList等标准实现;自定义List需确保重写了该方法 - 性能上与手写
Iterator基本一致,JVM会内联优化 - 不能在
removeIf的Predicate中修改原集合,否则行为未定义
示例:
list.removeIf(s -> s == null || s.trim().isEmpty());
多线程环境下遍历List必须用线程安全包装
即使用了Iterator.remove()或removeIf(),如果其他线程也在修改同一个List,依然会出错。
可行方案:
- 用
Collections.synchronizedList(new ArrayList()),但注意:迭代仍需手动同步list对象 - 改用
CopyOnWriteArrayList,适合读多写少场景;它的iterator()返回快照,遍历时允许其他线程修改,但removeIf()会加锁,且内存开销大 - 更稳妥的做法是:遍历前复制一份(
new ArrayList(original)),在副本上操作
容易忽略的一点:同步块必须包裹整个遍历过程,不只是remove()调用。










