• 执行ArrayList的remove(object)方法抛异常?


    简介

    或许有很多小伙伴都尝试过如下的代码:

    ArrayList<Object> list = ...;
    for (Object object : list) {
        if (条件成立) {
            list.remove(object);
        }
    }
    

    然后会发现抛出java.util.ConcurrentModificationException异常,这是一个并发异常。那么这个到底是什么情况?首先需要介绍一下增强for循环

    增强for循环

    增强for循环是Java1.5后,Collection实现了Iterator接口后出现的。增强for循环的代码如下

    for (Object object : list) {
        // 操作
    }
    

    其实增强for循环就是使用Iterator迭代器进行迭代的,增强for循环就变成下面这样:

    Iterator<Object> iterator = list.iterator();
    while (iterator.hasNext()) {
        iterator.next();
        // 操作
    }
    

    那么为什么在增强for循环中调用list.remove(object)会出事呢?那么咱们看看ArrayList下的 Iterator的实现类: Itr类

    Itr子类

    Itr子类是Iterator的实现类,属于ArrayList私有的局部内部类。我截取了Itr类的部分代码,如下:

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int expectedModCount = modCount;
    
        Itr() {}
    
        public boolean hasNext() {
            return cursor != size;
        }
    
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            ...
        }
        
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    

    elementData 是ArrayList存放元素的数组,上面代码没有贴出来。
    size 是elementData实际存放的容量大小
    modCount 记录elementData容量的修改次数
    expectedModCount 记录实例化迭代器Itr时,elementData容量的修改次数
    注意!:在迭代器中,当执行next方法的时候,会去调用checkForComodification方法,判断elementData 的容量是否被修改过。


    然后来看看ArrayList的remove(object)方法,截取部分代码如下:

    public boolean remove(Object o) {
        for (int index = 0; index < size; index++)
    	    if (找到目标元素) {
    	        fastRemove(index);
    	        return true;
    	    }
    	return false;
    }
    
    private void fastRemove(int index) {
        modCount++;
        // 移除操作
    }
    

    可以发现,调用remove(object)方法时调用了fastRemove方法,在fastRemove方法中执行modCount++
    现在把文章开头的代码拷下来,再来分析一次:

    ArrayList<Object> list = ...;
    for (Object object : list) {
        if (条件成立) {
            list.remove(object);
        }
    }
    

    当执行了list.remove时,执行modCount++ 。此时迭代器再往下进行迭代,执行了next方法,发现 modCount != expectedModCount,那么则抛出java.util.ConcurrentModificationException异常。 之所以Iterator认为是一个并发异常。是因为你不在迭代器里操作,而是在迭代器外面进行remove操作呀!
    难道没有其他解决方案吗?有滴。

    解决方案

    那么就是使用Itr的 remove方法。Itr子类重写了 remove 方法,这里部分代码:

    public void remove() {
        ...
        try {
            ArrayList.this.remove(需要删除元素的索引);
            ...
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    

    其实很简单,就是remove后,把 expectedModCount 同步一下 modCount 的值,这就解决了。完整代码如下:

    ArrayList<Object> list = ...;
    Iterator<Object> iterator = list.iterator();
    while (iterator.hasNext()) {
        iterator.next();
        iterator.remove();
    }
    

    总结

    本来我还不知道增强for循环是调用Iterator进行迭代的,要不是我debug了一波,我还不知道呐。还是小有收货。

    个人博客网址: https://colablog.cn/

    如果我的文章帮助到您,可以关注我的微信公众号,第一时间分享文章给您
    微信公众号

  • 相关阅读:
    RabbitMQ链接不上异常
    设计模式之禅之六大设计原则-迪米特原则
    <十二>面向对象分析之UML核心元素之节点和设备
    <十一>面向对象分析之UML核心元素之组件
    <十>面向对象分析之UML核心元素之关系
    <九>面向对象分析之UML核心元素之设计类,类,属性,方法,可见性
    <八>面向对象分析之UML核心元素之分析类
    <七>面向对象分析之UML核心元素之包
    <六>面向对象分析之UML核心元素之业务实体
    Spring Cloud(七):使用SVN存储分布式配置中心文件和实现refresh
  • 原文地址:https://www.cnblogs.com/Johnson-lin/p/13277592.html
Copyright © 2020-2023  润新知