Java ArrayList异常-ConcurrentModificationException

本文探讨了在Java中遍历List并尝试删除元素时出现的ConcurrentModificationException异常。通过示例代码展示了异常产生的原因,并提供了一种可行的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在操作List集合的时候,习惯用for each循环操作。这次项目中根据业务逻辑需要删除符合条件的元素,元素删除后,继续next操作,抛出了ConcurrentModificationException异常。下面,重现异常,看看异常是怎么发生的,怎么避免。

测试代码

public class ConcurrentModificationExceptionList {  
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        list1.add(4);
        for (Integer integer : list1) {
            if (integer == 1) {
                list1.remove(integer);
            }
        }
    }
}

异常的发生

ConcurrentModificationException异常是在这里抛出的。当modCount != expectedModCount为true的时候抛出。

这里写图片描述

这里写图片描述

原因

上述异常为什么会发生,来看一下源码中的删除动作。

这里写图片描述

这里写图片描述

在执行删除动作前modCount自加1。在下个元素做checkForComodification的时候异常就抛出了。

这里写图片描述

异常的解决

这里写图片描述

查看源码,modCount是在ArrayList的父类AbstractList中定义的,modCount记录list被修改的次数。在iterator和实现iterator的list中,进行next(),remove()、previous、set、add操作时,modCount的值被意外改变,将抛出异常ConcurrentModificationException。关于异常的解决,网上也有很多的方法,参考文末。

既然异常是在iterator和实现iterator的list中发生的,那不使用for each操作,采用for in操作就能避免异常的发生。

代码验证一下

        for (int i = 0; i < list1.size(); i++) {
            if (list1.get(i)==1){
                list1.remove(i);
                i--;//指向删除前的上一个元素
            }
        }

这里写图片描述

看一下源码:

这里写图片描述

源码中是没有做checkForComodification检查的,也不会发生异常。

参考

Java ConcurrentModificationException异常原因和解决方法
集合迭代时对集合进行修改抛ConcurrentModificationException原因的深究以及解决方案
Java ConcurrentModificationException 异常分析与解决方案

### JavaArrayList 发生 `ConcurrentModificationException` 的原因 在多线程环境中或单线程环境下,当尝试通过迭代器遍历 `ArrayList` 同时又对该列表进行了结构性修改(如添加或删除元素),则会触发 `ConcurrentModificationException`。这是因为每次调用迭代器的 `next()` 方法之前都会检查预期的修改次数 (`expectedModCount`) 是否等于实际发生的修改次数 (`modCount`);如果不相等就说明有其他地方改变了集合的状态从而抛出了该异常[^1]。 对于上述情况,在单线程下直接利用增强型for循环来访问并试图改变容器内的对象也会引发同样的问题,因为这种写法内部实际上也是基于迭代器实现的[^2]。 ### 解决方案 #### 单线程环境下的处理办法 针对单线程场景中的并发修改错误,推荐采用如下几种策略之一: - **使用 Iterator 的 remove() 方法** 当需要移除符合条件的元素时,应该借助于迭代器所提供的专用接口来进行操作而不是直接作用于原始数据结构上。下面给出了一段修正后的代码片段作为示范[^3]: ```java List<String> list = new ArrayList<>(); // ... 初始化list... Iterator<String> it = list.iterator(); while(it.hasNext()){ String element = it.next(); if(element.equals("target")){ it.remove(); // 安全地执行删除动作 } } ``` - **转换成同步列表** 可以考虑把普通的 `ArrayList` 替换成由 Collections.synchronizedList 工具类封装过的版本,这样可以确保每一次读取/更新都是原子性的,不过需要注意的是这仅适用于那些确实存在竞争条件的应用场合,并且可能会带来性能上的开销[^5]。 #### 多线程环境下的处理办法 考虑到 `ArrayList` 是非线程安全的设计,因此不适合应用于高并发度的任务当中。此时建议选用更合适的替代品——例如 CopyOnWriteArrayList 类型的数据结构,它能够在一定程度上缓解频繁写入带来的锁争用现象,进而提高整体效率。 ```java import java.util.concurrent.CopyOnWriteArrayList; public class SafeArrayListExample { private final List<Integer> safeList = new CopyOnWriteArrayList<>(); public void addElement(Integer e){ safeList.add(e); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无心水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值