美文网首页
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