迭代器的 remove 方法如何实际移除对象

2022-09-01 08:57:02

我们都知道,在迭代对象时从集合中删除对象的最安全的“并且可能唯一安全”的方法是首先检索 ,执行循环并在需要时删除;Iterator

Iterator iter=Collection.iterator();
while(iter.hasNext()){
    Object o=iter.next()
    if(o.equals(what i'm looking for)){
        iter.remove();
    }
}

我想了解的,不幸的是还没有找到一个深入的技术解释,是如何执行此删除,
如果:

for(Object o:myCollection().getObjects()){
    if(o.equals(what i'm looking for)){
        myCollection.remove(o);
    }
}

会抛出一个,“用技术术语”做什么?它是否删除对象,中断循环并重新启动循环?ConcurrentModificationExceptionIterator.remove()

我在官方文档中看到:

“删除当前元素。如果尝试调用之前未调用 next( )。”IllegalStateExceptionremove()

“删除当前元素”的部分,让我想起了在“常规”循环中发生的完全相同的情况=>(执行相等测试并在需要时删除),但是为什么迭代器循环 ConcurrentModification 是安全的?


答案 1

在迭代列表时无法修改列表的原因是因为迭代器必须知道要为 hasNext() 和 next() 返回什么。

如何做到这一点是特定于实现的,但你可以看看ArrayList/AbstractList/LinkedList等的源代码。

另请注意,在某些情况下,您可以使用类似如下的代码作为替代方法:

List<Foo> copyList = new ArrayList<>(origList);
for (Foo foo : copyList){
  if (condition){
    origList.remove(foo);
  }
}

但是此代码的运行速度可能会稍慢一些,因为必须复制集合(仅限浅副本),并且必须搜索要删除的元素。

另请注意,如果您直接使用迭代器,建议使用for循环而不是while循环,因为这会限制变量的范围:

for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){
...
}

答案 2

迭代器删除元素的确切方式取决于其实现,对于不同的集合,这可能有所不同。绝对不会破坏你所处的循环。我刚刚看了一下ArrayList迭代器是如何实现的,下面是代码:

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 remove 方法删除元素,并递增列表修改计数器,以便下次迭代时不会引发 ConcurrentModificationException。


推荐