java.util.ConcurrentModificationException在预期时不会抛出

以下代码抛出java.util.ConcurrentModificationException ,如预期的那样:

public void test(){ ArrayList myList = new ArrayList(); myList.add("String 1"); myList.add("String 2"); myList.add("String 3"); myList.add("String 4"); myList.add("String 5"); for(String s : myList){ if (s.equals("String 2")){ myList.remove(s); } } } 

但是,以下代码不会抛出exception,而我希望抛出它:

  public void test(){ ArrayList myList = new ArrayList(); myList.add("String 1"); myList.add("String 2"); myList.add("String 3"); for(String s : myList){ if (s.equals("String 2")){ myList.remove(s); } } } 

区别在于第一个列表包含5个项目,而第二个列表包含3.使用的JVM是:

 java version "1.8.0" Java(TM) SE Runtime Environment (build 1.8.0-b132) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode) 

问题:为什么第二段代码不会抛出java.util.ConcurrentModificationException?

在实现中从ArrayList.iterator()返回的迭代器我们显然都只使用对next()调用的结构修改检查, 而不是hasNext()调用。 后者看起来像这样(在Java 8下):

 public boolean hasNext() { return cursor != size; } 

所以在你的第二种情况下,迭代器“知道”它返回了两个元素,并且列表只有两个元素…所以hasNext()只返回false,我们永远不会第三次调用next()

我认为这是一个实现细节 – 基本上检查没有尽可能严格。 在这种情况下, hasNext()执行检查并抛出exception是完全合理的。

另请注意ArrayList文档摘要的最后一段:

失败快速迭代器会尽最大努力抛出ConcurrentModificationException 。 因此,编写依赖于此exception的程序以确保其正确性是错误的: 迭代器的快速失败行为应该仅用于检测错误。

如果您担心强制列表是只读的,请使用Collections.unmodifiableList方法,而不是检查ConcurrentModificationException,如上所述,不保证在所有相关情况下抛出。