从计数循环到迭代器中的列表中删除元素

Aft*_*ess 5 java

为什么这是合法的:

for(int i=0; i < arr.size(); i++) {
    arr.remove(i);
}
Run Code Online (Sandbox Code Playgroud)

但是使用迭代器或a的语法糖会导致ConcurrentModificationException:

for(String myString : arr) {
   arr.remove(myString);
}
Run Code Online (Sandbox Code Playgroud)
  • 在每个人开始跳上这个潮流之前告诉我使用iterator.remove();我问为什么不同的行为,而不是如何避免conc mod异常.谢谢.

Mif*_*eet 4

我们来看看eg的ArrayLists迭代器是如何实现的:

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

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

    public void remove() {
        // ...
        ArrayList.this.remove(lastRet);
        // ...
        cursor = lastRet;
        lastRet = -1;
    }
Run Code Online (Sandbox Code Playgroud)

让我们看一个例子:

List list = new ArrayList(Arrays.asList(1, 2, 3, 4));
Iterator it = list.iterator();
Integer item = it.next();
Run Code Online (Sandbox Code Playgroud)

我们删除第一个元素

list.remove(0);
Run Code Online (Sandbox Code Playgroud)

如果我们现在想调用it.remove(),迭代器将删除数字 2,因为这就是 fieldlastRet现在所指向的。

if (item == 1) {
   it.remove(); // list contains 3, 4
}
Run Code Online (Sandbox Code Playgroud)

这是不正确的行为!迭代器的契约规定remove()删除返回的最后一个元素next(),但在存在并发修改的情况下无法保持其契约。因此,为了安全起见,它选择抛出异常。

对于其他收藏品来说,情况可能更加复杂。如果您修改 a HashMap,它可能会根据需要增大或缩小。那时,元素将落入不同的存储桶,并且在重新散列之前保留指向存储桶的指针的迭代器将完全丢失。

请注意,它iterator.remove()本身不会引发异常,因为它能够更新自身集合的内部状态。然而,调用remove()同一实例集合的两个迭代器会抛出异常,因为这会使其中一个迭代器处于不一致的状态。


归档时间:

查看次数:

119 次

最近记录:

10 年,3 月 前