java.util.ConcurrentModificationException 未在预期时引发

2022-09-03 12:31:41

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

   public void test(){
      ArrayList<String> myList = new ArrayList<String>();

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

但是,下面的代码不会引发异常,而我希望它被抛出:

   public void test(){
      ArrayList<String> myList = new ArrayList<String>();

      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?


答案 1

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

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

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

我认为这是一个实现细节 - 基本上检查没有那么严格。在这种情况下,执行检查并引发异常是完全合理的。hasNext()


答案 2

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

快速失败的迭代器以尽力而为。因此,编写一个依赖于此异常的正确性的程序是错误的:迭代器的故障快速行为应该仅用于检测错误。ConcurrentModificationException

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