遍历Collection时删除元素

其实标题我想用《为什么foreach边循环边移除元素要用Iterator?》可是太长。

不用Iterator,用Collection.remove(),会报ConcurrentModificationException错误。

1
2
3
for(Integer i : list) {
  list.remove(i); //Throw ConcurrentModificationException
}

其实使用foreach的时候,会自动生成一个Iterator来遍历list。不只是remove,使用add、clear等方法一样会出错。

拿ArrayList来说,它有一个私有的Iterator接口的内部类Itr:

1
2
3
4
5
6
7
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;
  	
  //sevrval methods
}

使用Iterator来遍历ArrayList实际上是通过两个指针来遍历ArrayList底层的数组:cursor是下一个返回的元素在数组中的下标;lastRet是上一个元素的下标。还有一个重要的expectedModCount使用的是ArrayList的modCount的(modCount具体是什么意思下文会提到)。

从Itr的实现来看,有三种情况会抛出ConcurrentModificationException:

expectedModCount不等于modCount

开头所说的问题正是是第二种情况下出现的。modCount简单说记录了Collection被修改的次数:添加或者删除元素。

假如在foreach循环中删除元素,且此时modCount等2:

上面提到Iterator的实现中删除元素实际调用的还是ArrayList.remove()方法,为什么不会抛错?

Itr的remove方法在调用ArrayList.remove()之后,会更新expectedModCount

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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();
    }
}

Comments

comments powered by Disqus