美文网首页
ArrayList中遇到的ConcurrentModificat

ArrayList中遇到的ConcurrentModificat

作者: 路边摊要进大商场 | 来源:发表于2017-12-16 12:50 被阅读0次

在做项目的时候某一天遇到了ConcurrentModificationException异常。搜索了下英文的含义和网上博客的说法。想起了这个很容易忽略的问题,这里来给记录一下。

1.说一下问题发生的情景,如下代码,我们遍历ArrayList 并对符合要求的项删除。当要删除的index小于4的时候就会出现问题

List lists =newArrayList<>();
lists.add("1");
lists.add("2");
lists.add("3");
lists.add("4");
lists.add("5");
// 不正确的写法
for(String temp : lists) {
    if(temp.equals("2")) {
        lists.remove(temp);
    }
}

2.出现的原因

ArrayList的remove方法是使用fastRemove(其源码如下),从源码中可得到ArrayList每次remove的时候其实是数组的一个copy的过程,将删除的index前后的元素重新组成一个arrayList,并且modCount++。modCount类似一个arraylist被修改的计数器。每次add或remove都会被++

private void fastRemove(int index) {
    modCount++;
    int numMoved =size- index -1;
    if(numMoved >0)
        System.arraycopy(elementData, index+1,elementData, index,numMoved);
    elementData[--size] =null;// clear to let GC do its work
}

看完上面在继续找原因。for(String temp: list)实质是ArrayList的内部类Itr实现的。源码如下

private class Itr implements Iterator {
    int cursor;// index of next element to return
    int lastRet= -1;// index of last element returned; -1 if no such
    int expectedModCount=modCount;
    public boolean hasNext() {
        return cursor!=size;
    }
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i =cursor;
        if(i >=size)
            throw newNoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if(i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor= i +1;
        return(E) elementData[lastRet= i];
    }
.....
}

final void checkForComodification() {
    if(modCount!=expectedModCount) 
        throw newConcurrentModificationException();
}

我们发现modCount!=expectedModCount 不相同时会报异常。好像找到了原因。

我们梳理一下。我们先给lists add 了五个值,modCount =5 我们要遍历元素时,expectedModCount=modCount =5 但是当我们remove的时候modCount=6,再次遍历的时候两个值不相同了抛出异常。

3.额外的实验。假如我删除最后一个会有问题吗?这个大家自己先想,最后给答案吧。

4.有了问题原因,我们来解决问题吧。

既然用样的方式不行,我们何不回归原始。来试一试for(int i =0; i<list.size;i++)来删除呢哈哈。这样就没问题了。只不过要注意i的变化,因为你remove调一个其它的会替换他的位置。我相信你明白了这个方法。

其次,我就是要用itr行不行呢?答案是可以的,继续源码ArrayList的内部类Itr有个remove方法,而且expectedModCount=modCount;重新赋值了。我去那不就是说不会在抛异常了,对的你可以使用it自身的remove而不是arrayList的remove

public void remove() {
    if(lastRet<0)
        throw newIllegalStateException();
    checkForComodification();
    try{
        ArrayList.this.remove(lastRet);
        cursor=lastRet;
        lastRet= -1;
        expectedModCount=modCount;
    }catch(IndexOutOfBoundsException ex) {
        throw newConcurrentModificationException();
    }
}

好了,貌似现在完事了。突然有一天你发现程序在多线程的情况下还是有问题不兜圈子了,要么加上锁,要么用CopyOnWriteArrayList来替换线程不安全的ArrayList.

最后说一下之前遗留问题答案:删除最后一个元素不会抛异常。

相关文章

网友评论

      本文标题:ArrayList中遇到的ConcurrentModificat

      本文链接:https://www.haomeiwen.com/subject/qdsswxtx.html