【问题标题】:java.util.ConcurrentModificationException not thrown when expectedjava.util.ConcurrentModificationException 未按预期抛出
【发布时间】:2014-09-18 18:42:08
【问题描述】:

下面的代码如预期的那样抛出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)

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

【问题讨论】:

标签: java exception


【解决方案1】:

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

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

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

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

【讨论】:

  • @laune - 无论是否是疏忽,它都不是“错误”,因为实现符合规范。此外,自(至少)Java 6 以来,代码一直是这样的。
  • @laune 为什么 ArrayList 的 javadoc 不是规范?它指定了 ArrayList 的行为,包括 ConcurrentModificationException 不可靠这一事实。
  • @laune: 因为如果hasNext返回true,就会调用next,会检测到错误。
  • @laune hasNext 尝试检测并发修改是否会更好并不会改变这样一个事实,即根据类的规范,这种行为是不是错误。也许实施者有一些性能问题,或者只是简单地认为这种情况的罕见性有理由离开检查。也许他们根本没有想到。谁知道?无论哪种方式,这都不是错误。
  • @laune:是的,这正是我的观点——这就是为什么它没有被发现,但其他人没有。如果您要问一个更哲学的问题,即为什么要以这种方式实施,克里斯的评论说明了我想说的一切。
【解决方案2】:

还要注意ArrayList documentation summary的最后一段:

快速失败的迭代器将ConcurrentModificationException 抛出 尽力而为的基础。因此,编写程序是错误的 其正确性取决于此异常:快速失败 迭代器的行为应该只用于检测错误。

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

【讨论】:

    猜你喜欢
    • 2017-08-06
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多