在Java中,如何在单线程程序中出现ConcurrentModificationException?

6

我正在阅读这个 "Java 并发问题的常见解决方案" 的问题,并被一个回答中提到的 java.util.ConcurrentModificationException 所困惑。

我的理解是,这种情况可能发生在单线程程序中。 是什么条件导致以下代码抛出异常?

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

14

这段代码会一直抛出ConcurrentModificationException异常。

规则如下:在使用Iterator(例如使用for-each循环)遍历列表时,您不得修改(添加或删除元素)列表。

但是请注意,您可以通过迭代器修改列表(因为迭代器知道修改的内容,可以考虑到它),使用 Iterator.removeListIterator.add方法。

从文档中说明:

此类的iterator和listIterator方法返回的迭代器具有fail-fast特性:如果在创建迭代器后任何时候以除迭代器自己的remove或add方法之外的任何方式结构性修改列表,则迭代器都将抛出ConcurrentModificationException异常。

这里的concurrent一词指的是在遍历期间修改列表。


从技术上讲,只要使用迭代器的“remove”方法(但在for/each循环中不可用),您就可以在迭代列表时从中删除元素。 - Kevin Brock
@kevin 如果我修改迭代器结构而不使用remove方法,那么它会抛出ConcurrentModificationException吗?还是只有在使用迭代器的remove方法时才会抛出异常? - Dead Programmer
如果您使用Iterator.remove方法来修改集合,将不会抛出异常。 - aioobe

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个元素。因此,原来的第1个元素,现在变成了第0个元素,将永远不会被删除。


当然!我没想到,我一直在寻找更复杂的东西。 - ArturPhilibin
@DaSilva:我已经编辑并包含了更详细的解释。不过,我认为你已经明白了。 - Goran Jovic
我会修改这个答案的开头。它很容易被理解为“并发与线程无关”。第二句话是一个更好的开场白。 - user166390
@pst:你是对的。这确实令人困惑。 - Goran Jovic

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接