前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的ConcurrentModificationException异常原因分析及解决办法1/2/3/4

Java中的ConcurrentModificationException异常原因分析及解决办法1/2/3/4

原创
作者头像
齐天小胜
发布2023-02-14 17:02:52
5980
发布2023-02-14 17:02:52
举报
文章被收录于专栏:笔耕不辍

0.需求

从List列表中删除删除一个元素

#JDK版本

代码语言:txt
复制
java version "1.8.0_241"

1.现象

代码语言:java
复制
public class ListTest {

    List<String> list = new ArrayList<>();

    @Before
    public void before(){
        //初始化数据
        for (int i = 0; i < 6; i++) {
            list.add(String.valueOf(i));
        }
        //打印
        list.forEach(t->{
            System.out.println("val:" + t);
        });

    }

    @Test
    public void remove(){
        //删除元素"2"
        list.forEach(t -> {
            if("2".equals(t)){
                list.remove(t);
            }
        });
        //打印
        list.forEach(t->{
            System.out.println(t);
        });
    }
 }

运行后,发生如下异常:

代码语言:java
复制
java.util.ConcurrentModificationException
	at java.util.ArrayList.forEach(ArrayList.java:1260)
	at cn.pbdata.issue.manual.simple.ListTest.remove(ListTest.java:24)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

从异常信息可以发现,异常发生在java.util.ArrayList.forEach(ArrayList.java:1260)方法中。

查看ArrayList的源码:

代码语言:java
复制
@Override
public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    @SuppressWarnings("unchecked")
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        action.accept(elementData[i]);
    }
    //第1260行
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

其中,

modCount是ArrayList中的一个成员变量。从其中的注释说明中可以看到modCount表示对List的修改次数,每次调用add()方法或者remove()方法,就会对modCount进行加1操作。

在此例中,list最初有6个元素,那么最初的modCount=6,

我们进行了remove一次,所以 modCount=7,

而expectedModCount还是为6,导致modCount != expectedModCount,所以就出现了并发异常。

解决方案

如果要在List中删除元素,可以有以下几种办法:

1. 使用迭代器

我们重构之前的测试代码,使用 Iterator进行删除操作。在 Iterator迭代器中,可以使用remove()。测试代码如下:

代码语言:java
复制
    @Test
    public void removeV2(){
        //删除元素"2"
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String t = iterator.next();
            if("2".equals(t)){
                iterator.remove();
            }
        }

        //打印
        list.forEach(t->{
            System.out.println(t);
        });
    }

测试通过,运行正常。

这是因为迭代器在循环过程中调用是安全的,remove()方法就不会导致ConcurrentModificationException。

2. 迭代期间不删除

如果一定要使用for-each循环,那么我们可以再构建一个list,保存需要删除的元素,等到迭代结束后,再移除元素。测试代码如下:

代码语言:java
复制
    @Test
    public void removeV3(){
        //删除元素"2"
        List<String> toDelList = new ArrayList();

        list.forEach(t -> {
            if("2".equals(t)){
                toDelList.add(t);
            }
        });

        list.removeAll(toDelList);

        //打印
        list.forEach(t->{
            System.out.println(t);
        });
    }

这也是解决问题的一种方法。

3. 使用removeIf()

从Java 8开始,Collection接口引入了removeIf()方法。我们可以使用函数式编程的方法进行处理。测试代码如下:

代码语言:java
复制
    @Test
    public void removeV4(){
        //删除元素"2"
        list.removeIf(t -> "2".equals(t));

        //打印
        list.forEach(t->{
            System.out.println(t);
        });
    }

4. 使用函数式编程

还可以使用函数式编程中的流处理进行删除。测试代码如下:

代码语言:java
复制
    @Test
    public void removeV5(){
        //删除元素"2"
        List<String> afterList = list.stream()
                                        .filter(t -> (!"2".equals(t)))
                                        .map(Object::toString)
                                        .collect(Collectors.toList());

        //打印
        afterList.forEach(t->{
            System.out.println(t);
        });
    }

end!!!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0.需求
  • 1.现象
  • 解决方案
    • 1. 使用迭代器
      • 2. 迭代期间不删除
        • 3. 使用removeIf()
          • 4. 使用函数式编程
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档