30

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

 4 years ago
source link: http://www.cnblogs.com/Johnson-lin/p/13277592.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

简介

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

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/

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

m6Fzqeq.jpg!web

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK