美文网首页Java 杂谈
java遍历删除集合元素的三种方法以及ConcurrentMod

java遍历删除集合元素的三种方法以及ConcurrentMod

作者: MDZZ灭顶之灾12138 | 来源:发表于2018-09-22 15:30 被阅读0次

    JAVA中循环遍历list有三种方式for循环、foreach循环、iterator遍历。

    1、for循环遍历list

    for(int i = 0; i < list.size(); i++){
            if(list.get(i).equals("del")){
                list.remove(i);
            }
        }
    

    这种方式的问题在于,删除某个元素后,list的大小发生了变化,索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

    2、foreach循环

     for(String x:list){
            if (x.equals("del"))
                list.remove(x);
            }
        }
    

    这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错

    3、iterator遍历

    Iterator<String> it = list.iterator(); 
            while(it.hasNext()){
                String x = it.next();
                if (x.equals("del")) {
                    it.remove();
                }
            }   
        }
    

    这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。

    总结:
      (1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种
      (2)循环删除list中多个元素的,应该使用迭代器iterator方式

    分析java.util.ConcurrentModificationException,环境:jdk1.8.0
    AbstractList是ArrayList的父类,这两个类都有iterator()方法

    AbstractList代码片段:

    protected transient int modCount = 0;
    表示对List的修改次数,每次调用add()方法或者remove()方法就会对modCount进行加1操作

    If the value of this field changes unexpectedly, the iterator (or list
    iterator) will throw a {@code ConcurrentModificationException} in
    response to the {@code next}, {@code remove}, {@code previous},
    {@code set} or {@code add} operations.

    public Iterator<E> iterator() {
        return new Itr();
    }
    
    private class Itr implements Iterator<E> {
        int cursor = 0;//表示下一个要访问的元素的索引
        int lastRet = -1;//表示上一个访问的元素的索引
        int expectedModCount = modCount;//表示对ArrayList修改次数的期望值,它的初始值为modCount。
        public boolean hasNext() {
               return cursor != size();
        }
        public E next() {
               checkForComodification();
            try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
            } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
            }
        }
        public void remove() {
            if (lastRet == -1)
            throw new IllegalStateException();
               checkForComodification();
     
            try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
            }
        }
     
        final void checkForComodification() {
            if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        }
    }
    

    ArrayList的remove():

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
     
     
    private void fastRemove(int index) {
        modCount++;//对集合修改了一次
        int numMoved = size - index - 1;//remove后的数量
        if (numMoved > 0)
            /**
              *the source array.
               *starting position in the source array.
               *the destination array.
               *starting position in the destination data.
               *the number of array elements to be copied.
              */
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // Let gc do its work
    }
    

    原因:调用list.remove()方法导致modCount和expectedModCount的值不一致。
    使用for-each进行迭代也会出现这种问题。

    iterator的remove方法调用的就是list.remove()方法,但是它多了一个操作:
    expectedModCount = modCount;
    多线程的情况下,使用iterator也会出问题。
    建议:使用并发CopyOnWriteArrayList代替ArrayList和Vector。

    相关文章

      网友评论

        本文标题:java遍历删除集合元素的三种方法以及ConcurrentMod

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