在使用 Java 的 `ArrayList` 时,若在遍历过程中直接调用 `remove()` 方法删除元素,会抛出 `ConcurrentModificationException` 异常。这是因为在迭代过程中结构被修改,导致迭代器状态不一致。解决该问题的常见方式有三种:一是使用迭代器自身的 `remove()` 方法;二是使用 Java 8+ 的 `removeIf()` 方法;三是遍历集合的副本或使用并发集合类如 `CopyOnWriteArrayList`。选择合适的方法可有效避免并发修改异常,确保程序稳定运行。
1条回答 默认 最新
The Smurf 2025-06-25 16:45关注一、问题背景:为什么在遍历 ArrayList 时调用 remove() 会抛出 ConcurrentModificationException?
在 Java 中,`ArrayList` 是一个非线程安全的动态数组实现。当我们使用增强型 for 循环(如 `for (Object o : list)`)或迭代器进行遍历时,如果直接通过集合对象调用 `remove()` 方法删除元素,Java 会检测到结构修改,并抛出 `ConcurrentModificationException`。
这是由于 `ArrayList` 内部维护了一个名为 `modCount` 的计数器,每次结构发生变化(如添加、删除元素),该值都会递增。而迭代器在遍历时会记录初始的 `modCount` 值,并在每次操作前检查是否被修改。一旦发现不一致,则抛出异常。
List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); // 错误示例 for (String s : list) { if (s.equals("B")) { list.remove(s); // 抛出 ConcurrentModificationException } }二、解决方案分析与对比
解决上述问题有三种常见方式,分别适用于不同的场景:
- 使用 Iterator 自身的 remove():适用于需要精确控制删除逻辑的情况。
- 使用 Java 8+ 的 removeIf():简洁高效,适合批量删除满足条件的元素。
- 遍历副本或使用并发集合类:如 `CopyOnWriteArrayList`,适用于多线程环境或避免修改原集合。
方法 适用场景 线程安全 代码简洁度 Iterator.remove() 单线程遍历删除 否 中等 removeIf() Java 8+ 环境下的批量删除 否 高 遍历副本 / CopyOnWriteArrayList 多线程读写场景 是 低 三、详细解决方案与代码示例
3.1 使用 Iterator 自身的 remove() 方法
这种方式由迭代器自己管理删除操作,不会触发 `modCount` 不一致的问题。
Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String s = iterator.next(); if (s.equals("B")) { iterator.remove(); // 安全删除 } }3.2 使用 Java 8+ 的 removeIf()
`removeIf(Predicate filter)` 是一种函数式编程风格的方法,可以非常简洁地表达删除逻辑。
list.removeIf(s -> s.equals("B")); // Java 8+3.3 遍历副本或使用并发集合类
若不想修改原集合,可以遍历其副本;或者在多线程环境下使用线程安全的 `CopyOnWriteArrayList`。
// 遍历副本 for (String s : new ArrayList<>(list)) { if (s.equals("B")) { list.remove(s); } } // 使用 CopyOnWriteArrayList List<String> threadSafeList = new CopyOnWriteArrayList<>();四、深入理解:从源码角度分析 ConcurrentModificationException 的机制
以 `ArrayList` 的 `Itr` 迭代器为例,在调用 `next()` 或 `remove()` 之前,都会执行如下校验:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }其中 `expectedModCount` 是迭代器初始化时保存的 `modCount` 值。当外部通过 `list.remove()` 修改了集合结构,导致 `modCount` 增加,但迭代器并不知道这一变化,就会抛出异常。
五、扩展思考:不同集合类的行为差异
需要注意的是,不同集合类对并发修改的处理策略不同。例如:
- `HashMap` 在迭代过程中也禁止结构性修改,否则也会抛出 `ConcurrentModificationException`。
- `ConcurrentHashMap` 允许并发修改,适合高并发环境。
- `CopyOnWriteArrayList` 在写操作时复制底层数组,适合读多写少的场景。
六、Mermaid 流程图展示解决方案选择路径
graph TD A[开始] --> B{是否使用 Java 8+?} B -- 是 --> C[使用 removeIf()] B -- 否 --> D{是否为单线程环境?} D -- 是 --> E[使用 Iterator.remove()] D -- 否 --> F[考虑使用 CopyOnWriteArrayList]本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报