关于for each循环不能直接使用集合的remove方法的原因

来源:互联网 发布:anywhere远程桌面软件 编辑:程序博客网 时间:2024/05/17 07:06

记录一下为何在使用for each 循环时,不能在循环体内直接使用集合的remove方法的原因;


因为for each循环的底层原理时用iterator来实现的,所以在遍历元素的时实际操作使用的时iterator;


但我们在循环体内部使用集合自身的remove方法时,以ArrayList源码为例:


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;    }


可以看见实际调用的时集合自身fastRemove(index)方法,我们继续看fastRemove的源码:

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    }

可以看见源码里面有个modCount++,该属性时ArrayList自身属性 默认为0,代表修改的次数,每次删除modCount都会自增;


因为for each底层原理时iterator,所以我们再看下iterator中hasnext、next方法源码:

public E next() {            checkForComodification();            int i = cursor;            if (i >= size)                throw new NoSuchElementException();            Object[] elementData = ArrayList.this.elementData;            if (i >= elementData.length)                throw new ConcurrentModificationException();            cursor = i + 1;            return (E) elementData[lastRet = i];        }

其中关注下checkForComodification()方法 ,其源码:

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

其中有个expectedModCount  该参数时iterator中的属性 ,代表使用iterator修改集合元素的次数;

当集合属性modCount 于expectedModCount次数不相等,则会抛异常;

当我们再for each中直接使用集合的remove方法时 modCount增加了,但是iterator并没有增加导致抛异常;

如果我们使用iterator再for each循环体中使用remove 不会抛异常的原因可以通过iterator中remove源码可知:

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

其中ArrayList.this.remove(lastRet) 调用的就是集合本身的remove方法 :

public E remove(int index) {        rangeCheck(index);        modCount++;        E oldValue = elementData(index);        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        return oldValue;    }

可以看见modCount会自增,所以在调用iterator的remove方法时modCount等于expectedModCount;


综上,则是再for each循环体不能直接使用集合的remove方法但能用iterator的remove方法的原因。