在增强 for 中直接调用原集合的 remove,可能触发 ConcurrentModificationException。它不一定涉及另一个线程:同一线程绕过当前迭代器修改集合,也可能违反迭代约定。

问题是修改路径,不是禁止所有遍历删除

下面这类写法不可靠:

for (String value : values) {
    if ("expired".equals(value)) {
        values.remove(value);
    }
}

对于常见 ArrayList、HashSet 的 fail-fast 迭代器,应通过当前迭代器提供的删除路径修改,不能依赖“某次没报错”证明直接 remove 安全。

使用同一个迭代器删除刚返回的元素

以下是 Java 8 可修改 List 的完整小例子:

import java.util.*;

public class IteratorRemoveDemo {
    public static void main(String[] args) {
        List<String> values =
            new ArrayList<>(Arrays.asList("keep", "expired", "expired", null));
        Iterator<String> iterator = values.iterator();
        while (iterator.hasNext()) {
            String value = iterator.next();
            if (value == null || "expired".equals(value)) {
                iterator.remove();
            }
        }
        System.out.println(values); // [keep]
    }
}

remove 只能删除最近一次 next 返回的元素,每次 next 后最多调用一次。它还是可选操作:不可修改集合、固定大小列表或某些迭代器可能抛 UnsupportedOperationException。Java 8 Iterator

在支持的可修改集合上,Java 8 的 removeIf 也可表达条件删除,但仍不是任意集合都支持。改写循环时保持业务条件不变,不要同时把 state 换成另一个状态字段。

迭代器模式统一的是调用方式

迭代器把“取得下一个元素”的机制与集合内部表示分开。调用者不必为每一种存储结构写一套遍历逻辑,也不必把集合的内部节点暴露出去。

Java 的 Iterable 提供 iterator,使对象能作为增强 for 的目标;数组也能使用增强 for,但这不意味着数组实现了 Iterable。统一接口也不保证统一顺序、删除能力或快照语义。

fail-fast 不是并发正确性的保证

ConcurrentModificationException 用来尽力发现错误,不保证检测到所有竞态,也不应作为业务控制流。真有多线程共享修改时,需要按具体集合选择同步、快照或并发容器,不能只把 for 换成 while。异常的官方说明