唯一真正安全的单线程原地删除方式是 iterator.remove(),且必须紧接在 next() 之后调用;for-each 和普通 for 循环中调用 list.remove() 会因 modCount 不一致触发 ConcurrentModificationException;并发场景下 iterator.remove() 仍不安全,需选用 CopyOnWriteArrayList、synchronizedList 或 ConcurrentHashMap 等线程安全方案。

遍历集合时直接删元素,ConcurrentModificationException 几乎必现;唯一真正安全的单线程原地删除方式,是用 iterator.remove(),且必须紧接在 next() 之后调用。
为什么 for-each 和普通 for 循环里调 list.remove() 会崩
增强 for 循环本质就是 Iterator 的语法糖,但你拿不到迭代器引用,无法调 remove();一旦在循环体里写 list.remove(s),集合的 modCount 就和迭代器缓存的 expectedModCount 对不上,立刻抛 ConcurrentModificationException。
普通正向 for (int i = 0; i 中调 <code>list.remove(i) 不会抛这个异常,但会导致后续元素索引前移,而 i 仍自增,结果跳过下一个元素——比如删掉索引 1 的元素后,原索引 2 的元素移到了 1,但 i 已变成 2。
- 错误示例:
for (String s : list) { if (s.isEmpty()) list.remove(s); }→ 运行时崩 - 错误示例:
for (int i = 0; i → 可能漏删
iterator.remove() 的调用时机和限制
这个方法不是“只要用了就安全”,它有明确的状态约束:必须在 next() 返回一个元素后、且尚未再次调用 next() 前执行。否则抛 IllegalStateException。
- ✅ 正确:每次
next()后最多调一次remove() - ❌ 错误:连续两次
it.remove()(中间没next()) - ❌ 错误:还没调
next()就先调remove() - ❌ 错误:对
Collections.unmodifiableList()返回的集合调用remove()→ 抛UnsupportedOperationException
示例:
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(Predicate) 底层就是封装了 Iterator.remove(),语义清晰、代码短,日常推荐优先用。
- ✅ 安全:不会抛
ConcurrentModificationException - ✅ 性能:对
ArrayList是O(n),会触发数组收缩;对LinkedList是O(n),无拷贝开销 - ❌ 不支持获取被删元素本身(如需记录日志或统计,得先
stream().filter().collect()再删) - ❌
Predicate里不能再操作原集合(比如嵌套调removeIf),否则仍会抛异常
示例:list.removeIf(s -> s == null || s.isEmpty());
并发场景下 iterator.remove() 依然不安全
即使用了 iterator.remove(),如果其他线程同时修改同一集合(比如另一个线程调用 list.add()),仍可能抛 ConcurrentModificationException。这不是 bug,是 fail-fast 的设计意图——它只保证单线程下的结构一致性检查,不提供并发安全。
真正需要多线程安全删除,不能只靠迭代器:
- 读多写少:用 CopyOnWriteArrayList(注意每次写都复制整个数组)
- 写操作集中:用 Collections.synchronizedList(new ArrayList()),但遍历时仍需手动 synchronized(list) 块包裹
- 高并发 + 复杂逻辑:改用 ConcurrentHashMap 的 keySet() 视图(支持 removeIf),或显式锁控制临界区

















