5

我想知道为什么我不能从列表中删除元素,当我使用 foreach 循环迭代它时:

List<Object> objects = new ArrayList<Object>();
Object one = new Object();
Object two = new Object();
Object three = new Object(); 

objects.add(one);
objects.add(two);
objects.add(three);

然后删除以下元素:

foreach(Object o : objects){
  objects.remove(three); //I know that o is my current object
}

似乎 foreach 循环不允许删除循环队列中“仍然”的对象。我对么?

为什么 for-int-loop 不关心这个?在这个循环中,我可以轻松删除仍在循环中的对象。

谢谢

4

4 回答 4

4

好吧,最初这个问题被标记为 C#,现在被删除了。然而....

如果你从集合中删除一个元素,for/loop 并不关心。如果在前进的过程中删除元素,则在删除元素之后索引元素会出现问题,因为索引不再匹配您的下一个元素。只有以相反的顺序循环(从最后一个元素到第一个元素),才能避免 for/loop 出现问题

foreach 在内部也遇到了同样的问题。枚举器维护一个Current元素的位置,如果你删除元素然后前进到该Next位置,Current索引器不会指向删除后的元素,而是指向删除后的第二个元素。

例如:

假设有“birds”、“animals”和“fishes”的字符串集合

  • 在 foreach 开始时,内部电流为 0(指向“鸟”)
  • 你删除“鸟”,所以“动物”在索引 0(当前仍然指向)
  • 您前进到下一个元素(当前=1,指向“鱼”)
  • 无一例外,Next Advance 将退出循环。

foreach 的设计并未允许这种情况。

于 2013-05-27T23:12:57.303 回答
2

就 Java 而言,答案在javadoc中(重点是我的):

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

于 2013-05-27T23:07:12.110 回答
1

参见Calling remove in foreach loop in Java,一般使用 Iterator 并检查 hasNext 是否有更多元素

于 2013-05-27T23:07:02.720 回答
1

至少对于 Java,您是正确的:问题是用于遍历列表中元素的迭代器不允许修改。它将抛出 ConcurrentModificationException。引用java.util.ArrayList的 Java API javadoc

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

另一种方法是使用允许此类修改的迭代器,而不是 ArrayList 的默认值,或者显式创建迭代器,而不是使用 foreach 循环,并使用它在迭代列表时更改内容的方法。

于 2013-05-27T23:12:04.463 回答