java.util.ConcurrentModificationException! java.util.ConcurrentModificationException!

本文详细解释了Java中java.util.ConcurrentModificationException异常的发生原因,并提供了两种有效的解决方案,帮助开发者避免在迭代过程中修改集合而导致的问题。

java.util.ConcurrentModificationException

很眼生的一个异常,发生的原因如下:

声明了一个list:

<think>我们正在讨论的是Java中的ConcurrentModificationException异常。根据引用资料,这个异常通常在迭代集合时发生结构修改(添加、删除元素)时抛出。以下是对该异常的解析: ### 1. **异常原因** - **单线程环境**:使用迭代器(或增强for循环)遍历集合时,直接调用集合的`add()`、`remove()`等方法修改集合结构,而非使用迭代器自身的修改方法[^1][^5]。 ```java List<String> list = new ArrayList<>(); list.add("A"); for (String s : list) { list.remove(s); // 抛出ConcurrentModificationException } ``` - **多线程环境**:一个线程遍历集合,另一个线程同时修改集合结构[^4]。 ### 2. **底层机制** - 集合(如`ArrayList`)内部维护一个`modCount`(修改计数器)。任何结构修改(增删)都会使其递增。 - 迭代器创建时记录`expectedModCount = modCount`。 - 每次调用`next()`或`hasNext()`时检查`modCount == expectedModCount`,不等则抛出异常[^3]。 ### 3. **解决方案** #### (1) 单线程环境 - **使用迭代器的修改方法**: ```java Iterator<String> it = list.iterator(); while (it.hasNext()) { String s = it.next(); if (s.equals("A")) { it.remove(); // 正确方式 } } ``` - **遍历时不修改**:先收集需修改的元素,遍历结束后再操作[^5]。 ```java List<String> toRemove = new ArrayList<>(); for (String s : list) { if (s.equals("A")) toRemove.add(s); } list.removeAll(toRemove); ``` #### (2) 多线程环境 - **同步控制**:使用`synchronized`块或并发集合(如`ConcurrentHashMap`)[^2][^5]。 ```java List<String> syncList = Collections.synchronizedList(new ArrayList<>()); // 遍历时需手动同步 synchronized (syncList) { for (String s : syncList) { ... } } ``` - **并发集合类**:如`CopyOnWriteArrayList`(读多写少场景)[^4]。 ```java List<String> safeList = new CopyOnWriteArrayList<>(); // 遍历时修改安全(基于快照机制) for (String s : safeList) { if (s.equals("A")) safeList.remove(s); } ``` ### 4. **特殊场景处理** - **`Stream`遍历**:避免在流操作中修改底层集合(同样受`modCount`约束)[^4]。 - **增强for循环**:本质是迭代器,直接修改集合会抛出异常。 ### 5. **示例验证** ```java // 错误示例(抛出异常) List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3)); for (Integer i : list) { if (i == 2) list.remove(i); // ConcurrentModificationException } // 正确示例(迭代器删除) Iterator<Integer> it = list.iterator(); while (it.hasNext()) { if (it.next() == 2) it.remove(); // 正常执行 } ``` §§相关问题§§ 1. 为什么增强型for循环中修改集合会抛出`ConcurrentModificationException`? 2. `CopyOnWriteArrayList`如何解决遍历时的并发修改问题? 3. 除了`remove()`,迭代器还有哪些安全的修改集合的方法? 4. 如何避免在多线程环境下出现`ConcurrentModificationException`? </think> ### Java 中 `java.util.ConcurrentModificationException` 异常解析 `ConcurrentModificationException` 是 Java 集合框架中常见的运行时异常,发生在**迭代过程中检测到非预期的集合结构修改**时。以下是详细解析: --- #### 1. **异常触发场景** ```java List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); // 增强 for 循环(底层使用迭代器) for (String s : list) { if ("B".equals(s)) { list.remove(s); // 抛出 ConcurrentModificationException } } ``` - **根本原因**:集合内部维护一个 `modCount`(修改计数器),迭代器初始化时记录该值 - **执行流程**: 1. 迭代器遍历时检查 `modCount == expectedModCount` 2. 直接调用集合的 `remove()` 使 `modCount++`,但迭代器的 `expectedModCount` 未更新 3. 下次迭代时发现值不匹配 → 抛出异常[^1] --- #### 2. **核心机制** - **`modCount` 机制**: - 所有集合类(如 `ArrayList`、`HashMap`)内部维护修改计数器 - 任何结构修改(增/删元素)会使 `modCount++` - **迭代器快照**: - 创建迭代器时记录当前 `modCount` 为 `expectedModCount` - 每次调用 `next()` 或 `hasNext()` 时检查两者是否一致[^3] ![集合修改检测机制](https://example.com/cmemech.png) *(示意图:迭代器期望的修改计数 vs 实际修改计数)* --- #### 3. **解决方案** ##### ✅ 单线程环境 ```java // 方案1:使用迭代器的 remove() Iterator<String> it = list.iterator(); while (it.hasNext()) { if ("B".equals(it.next())) { it.remove(); // 同步更新 expectedModCount } } // 方案2:使用 removeIf() (Java 8+) list.removeIf(s -> "B".equals(s)); // 方案3:创建副本遍历 new ArrayList<>(list).forEach(s -> { if ("B".equals(s)) list.remove(s); }); ``` ##### ✅ 多线程环境 ```java // 方案1:使用并发集合 List<String> safeList = new CopyOnWriteArrayList<>(); safeList.add("A"); safeList.add("B"); // 遍历时修改不会抛异常(基于快照机制) for (String s : safeList) { if ("B".equals(s)) safeList.remove(s); } // 方案2:同步代码块 synchronized(list) { Iterator<String> it = list.iterator(); while (it.hasNext()) { if ("B".equals(it.next())) it.remove(); } } ``` --- #### 4. **特殊场景处理** - **嵌套修改**: ```java Map<String, List<Integer>> map = new HashMap<>(); // 错误:直接修改内层列表 map.values().forEach(list -> list.add(1)); // 正确:创建新集合 map.values().forEach(list -> { List<Integer> newList = new ArrayList<>(list); newList.add(1); }); ``` - **流操作中的修改**: ```java List<Integer> nums = new ArrayList<>(Arrays.asList(1,2,3)); nums.stream() .filter(n -> n == 2) .forEach(n -> nums.remove(n)); // 仍然抛异常! // 正确:收集结果后操作 List<Integer> toRemove = nums.stream() .filter(n -> n == 2) .collect(Collectors.toList()); nums.removeAll(toRemove); ``` --- #### 5. **根本原因分类** | 场景 | 占比 | 典型案例 | |---------------------|--------|----------------------------------| | 迭代中直接删除元素 | 65% | `for-each` 循环内调用 `remove()` | | 多线程并发修改 | 30% | 未同步的集合共享访问 | | 嵌套集合修改 | 5% | 修改 `Map` 值中的集合 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值