6

我正在阅读这个“ Freuqent Java concurrency questions”问题,并被一个关于java.util.ConcurrentModificationException的答案弄糊涂了。

我对答案的理解是,这可能发生在单线程程序中。如何或什么条件导致以下代码抛出异常?

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }
4

3 回答 3

14

这个片段总是会抛出一个ConcurrentModificationException.

规则如下:在使用 Iterator 迭代它时,您不能修改(从列表中添加或删除元素)(当您使用 for-each 循环时会发生这种情况)。

Iterator.remove但是请注意,您可以使用or来通过迭代器修改列表(因为它知道修改,并且可以将其考虑在内)ListIterator.add

从文档:

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。

这里的并发一词是指您在遍历期间正在修改列表。

于 2011-02-19T16:20:02.567 回答
2

循环遍历列表时,您正在更改列表。

这里ConcurrentModificationException与线程无关。

警告!下面的示例展示了在更改集合时使用集合是多么危险。这不是在 OP 的情况下到底发生了什么(通过迭代器循环并改变)

我认为如果您使用带有这样的计数器的老式循环,更容易想象它有什么问题:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));

for (int i = 0; i < list.size(); i++) { 
   list.remove(i);  //removes element at given index
} 

现在,第一次i是 0,第 0 个元素被删除。第二次i是 1,所以现在第一个元素被删除。但是,在第一次移除之前,现在是第 1,曾经是第 2。所以,曾经是第 1 次,现在是第 0 次的,永远不会被删除。

于 2011-02-19T16:18:55.903 回答
2

因为您正在从列表中删除并同时使用迭代器对其进行迭代。

使用迭代器查看等效代码。

import java.lang.*;
import java.util.*;

class Test{
  public static void main(String[] argv){
      List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
      Iterator<String> itr=list.iterator();
      while(itr.hasNext()){
          String a=itr.next();
           list.remove(a);
      }
   }
}

正确的代码是

import java.lang.*;
import java.util.*;

class Test{
  public static void main(String[] argv){
      List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
      Iterator<String> itr=list.iterator();
      while(itr.hasNext()){
          itr.remove();
      }
   }
}
于 2011-02-19T16:31:08.330 回答