我是靠谱客的博主 秀丽茉莉,最近开发中收集的这篇文章主要介绍Java中的ConcurrentModificationException异常原因分析及解决办法1/2/3/4,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

0.需求

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

#JDK版本
java version "1.8.0_241"

1.现象

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.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的源码:

@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()。测试代码如下:

    @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,保存需要删除的元素,等到迭代结束后,再移除元素。测试代码如下:

    @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()方法。我们可以使用函数式编程的方法进行处理。测试代码如下:

    @Test
    public void removeV4(){
        //删除元素"2"
        list.removeIf(t -> "2".equals(t));

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

4. 使用函数式编程

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

    @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!!!

最后

以上就是秀丽茉莉为你收集整理的Java中的ConcurrentModificationException异常原因分析及解决办法1/2/3/4的全部内容,希望文章能够帮你解决Java中的ConcurrentModificationException异常原因分析及解决办法1/2/3/4所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部