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

Gij*_*iet 15 java exception

以下代码按预期抛出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);
         }
      }
   }
Run Code Online (Sandbox Code Playgroud)

但是,下面的代码并没有抛出异常,而我希望它被抛出:

   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);
         }
      }
   }
Run Code Online (Sandbox Code Playgroud)

区别在于第一个列表包含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)
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 18

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

public boolean hasNext() {
    return cursor != size;
}
Run Code Online (Sandbox Code Playgroud)

因此,在你的第二个情况下,迭代器"知道",它的返回两个元素,并且该列表仅具有两个元素...所以hasNext()刚刚返回false,我们从来没有最终调用next()的第三次.

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

  • @laune - 无论是否是疏忽,它都不是"bug",因为实现符合规范.除了(至少)Java 6以外,代码就像这样. (4认同)
  • @laune对于`hasNext`尝试检测并发修改是否会更好*不会改变这样的事实:根据类的规范,这种行为不是错误.也许实施者有一些性能问题,或者只是普遍认为这种情况的罕见性证明离开支票是合理的.也许他们根本没有想到它.谁知道?无论哪种方式,它都不是一个错误. (4认同)
  • @laune为什么ArrayList的javadoc不是规范?它指定了ArrayList的行为,包括ConcurrentModificationException不可靠的事实. (2认同)
  • @laune:是的,这正是我的观点 - 这就是它未被发现的原因,但其他人却没有.如果你问的是更为哲学的问题,为什么它以这种方式实施,克里斯的评论说我会说. (2认同)

Pet*_* O. 8

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

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

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