zoukankan      html  css  js  c++  java
  • 执行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/

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

  • 相关阅读:
    HDU 3951 (博弈) Coin Game
    HDU 3863 (博弈) No Gambling
    HDU 3544 (不平等博弈) Alice's Game
    POJ 3225 (线段树 区间更新) Help with Intervals
    POJ 2528 (线段树 离散化) Mayor's posters
    POJ 3468 (线段树 区间增减) A Simple Problem with Integers
    HDU 1698 (线段树 区间更新) Just a Hook
    POJ (线段树) Who Gets the Most Candies?
    POJ 2828 (线段树 单点更新) Buy Tickets
    HDU 2795 (线段树 单点更新) Billboard
  • 原文地址:https://www.cnblogs.com/Johnson-lin/p/13277592.html
Copyright © 2011-2022 走看看