如何在迭代Hashmap时获得ConcurrentModificationException?

时间:2013-08-26 17:35:40

标签: java collections iterator hashmap

我正在尝试将键值对添加到Iterator方法中的hashmap。

但这不是给我ConcurrentModificationException。为什么呢?

由于Hashmap是无法阻止的。

Map<String,String> m = new HashMap<>();
           m.put("a", "a");

           Iterator<String> i = m.keySet().iterator();
           while(i.hasNext()){
               System.out.println(i.next());
               m.put("dsad", "asfsdf");

           }

如果这是错误的,我怎么能产生ConcurrentModificationException? 感谢。

更新:刚刚检查过。

Map<String,String> m = new HashMap<>();
               m.put("a", "a");
          m.put("abc", "a");

               Iterator<String> i = m.keySet().iterator();
               while(i.hasNext()){
                   System.out.println(i.next());
                   m.put("dsad", "asfsdf");

               }

这给了我例外。

3 个答案:

答案 0 :(得分:3)

HashMap代码执行的并发修改检查无法检测到这种情况。 Oracle JDK7中HashMap的迭代器hasNext的代码是:

public final boolean hasNext() {
    return next != null;
}

...其中(令人困惑!)next是迭代器类中的私有数据成员(不要与next接口上的Iterator方法混淆 - 到我的请注意,调用该数据成员next非常糟糕的选择。)

请注意,它不会检查并发修改。与({间})Iterator#next

调用的代码形成对比
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

... 进行检查。

所以这是你的代码中发生的事情:

  1. 您创建了HashMap
  2. 您可以添加一个条目。
  3. 你开始迭代。
  4. hasNext是真的,所以你进入循环体。
  5. 你从next获得元素;此时,迭代器会记住下一个元素应该在其内部数据成员上的内容(容易引起混淆的next),在这种情况下,因为地图中没有下一个元素,所以next数据member设置为null,表示迭代已完成。
  6. 您可以添加到地图中。
  7. 您的代码调用hasNext,其中next数据成员为null并返回false
  8. 如果你在开始循环之前在地图中有两个元素而不是一个,你就会得到异常(来自next)。

    我之前认为这是一个或几乎是一个错误,但它是一个非常模糊的领域,而其他人则相当合理地认为它不是。该文档没有具体说明Iterator<E>的哪些方法会引发异常,只是它会被抛出。该文档还说它只是在“尽力而为”的基础上投入,但并不能保证。

    无论是否认为这是一个错误,此时不太可能改变,因为改变它的痛苦(打破一些可能不应该依赖于这种行为的现有代码)远远超过了利益(可能是更“正确”)。

答案 1 :(得分:0)

迭代器可能抛出ConcurrentModificationException,但不保证。

来自HashMap的javadoc:

  

请注意,无法保证迭代器的快速失败行为   一般来说,不可能做出任何硬性保证   存在不同步的并发修改。快速失败   迭代器抛出ConcurrentModificationException就是尽力而为   基础。因此,编写一个依赖的程序是错误的   关于它的正确性的这个例外:快速失败的行为   迭代器应该只用于检测错误。

答案 2 :(得分:0)

试试这个:

  Map<String,String> m = new HashMap<>();
    m.put("a", "a");

    Iterator<String> i = m.keySet().iterator();
    while(i.hasNext()){
        m.remove("a");
        System.out.println(i.next());


    }