【问题标题】:How I can get ConcurrentModificationException while iterating Hashmap?迭代Hashmap时如何获得ConcurrentModificationException?
【发布时间】:2013-08-29 06:33:30
【问题描述】:

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

但这并没有给我 ConcurrentModificationException 。为什么?

因为 Hashmap 是 failfast。

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

               }

这给了我一个例外。

【问题讨论】:

  • 您的问题是在遍历地图时调用m.puti 应该去dsad 的关键,还是不去?

标签: java collections iterator hashmap


【解决方案1】:

HashMap 代码所做的并发修改检查碰巧未能检测到这种情况。 Oracle的JDK7中HashMap的迭代器hasNext的代码为:

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

...哪里(令人困惑!)next 是迭代器类中的私有数据成员(不要与 Iterator 接口上的 next 方法混淆——在我看来,调用该数据成员next 是一个非常糟糕的选择)。

请注意,它不会检查并发修改。与从Iterator#next(间接)调用的这段代码对比:

    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

...进行检查。

以下是您的代码中发生的情况:

  1. 您创建了一个HashMap
  2. 您向其中添加一个条目。
  3. 您开始迭代。
  4. hasNext 是真的,所以你进入你的循环体。
  5. 你从next获取元素;此时,迭代器会记住其内部数据成员上的下一个元素(名称容易混淆的next),在这种情况下,由于映射中没有下一个元素,因此next 数据成员设置为null,表示迭代完成。
  6. 您添加到地图中。
  7. 您的代码调用hasNext,它看到next 数据成员是null 并返回false

如果在开始循环之前地图中有两个元素而不是一个元素,则会出现异常(来自 next)。

我之前曾争论过这是或几乎是一个错误,但它是一个相当模糊的领域,其他人相当合理地认为它不是。文档没有具体说明Iterator&lt;E&gt; 的哪些方法会抛出异常,只是说它会被抛出。文档还说它只是在“尽力而为”的基础上抛出的,不能保证。

无论是否认为这是一个错误,在这一点上都不太可能改变它,因为改变它(破坏一些可能不应该依赖这种行为的现有代码)的痛苦远远超过了好处(可能是更“正确”)。

【讨论】:

  • 为什么这不是错误?
  • @Raj:嗯,认为它一个错误。我已经用一些推理更新了答案,并对您的示例中发生的事情进行了更深入的解释。
【解决方案2】:

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

来自HashMap的javadoc:

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

【讨论】:

    【解决方案3】:

    试试这个:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      • 1970-01-01
      • 2014-02-25
      • 1970-01-01
      • 2017-11-11
      • 2017-11-27
      相关资源
      最近更新 更多