使用foreach循环时没有ConcurrentModificationException

时间:2019-12-25 14:56:20

标签: java iterator

我正在尝试运行以下代码:

List<String> l = new ArrayList<String>();
l.add("jim");
l.add("pam");

for(String s : l) {
    l.remove(s);
}

但是我没有任何例外。不是每个循环都使用Iterator,并且我们不能在迭代时更改List的结构吗?

编辑:

当列表包含3个以上元素时,我得到ConcurrentModificationException。为什么会这样?

1 个答案:

答案 0 :(得分:3)

之所以没有ConcurrentModificationException是因为您的列表仅包含两个元素,这阻止了实现检测到并发修改。这是Iterator使用的Java 13 ArrayList实现:

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;

    // prevent creating a synthetic constructor
    Itr() {}

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    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];
    }

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

    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i < size) {
            final Object[] es = elementData;
            if (i >= es.length)
                throw new ConcurrentModificationException();
            for (; i < size && modCount == expectedModCount; i++)
                action.accept(elementAt(es, i));
            // update once at end to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
    }

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

如您所见,它在next()方法中检查并发修改,但不在hasNext()方法中检查。这个很重要。如果您不知道,则增强的for循环在幕后使用Iterator。换句话说,您的for-each循环等效于:

for (Iterator<String> itr = l.itr(); itr.hasNext(); ) {
    String e = itr.next();
    l.remove(e);
}

一旦循环删除了第一个元素,列表中只剩下一个元素。但是,该元素已经向下移动了一个索引(1→0),但是迭代器的光标增加了一个。这使光标等于大小,因此循环不会第二次执行。由于循环不会第二次执行,因此不会再次调用next()方法,也不会进行并发修改检查。这样做还有一个副作用,就是将第二个元素保留在列表中。

一旦列表中包含三个或更多元素,循环便会再次执行 ,从而使它可以检测到并发的修改。