代码之家  ›  专栏  ›  技术社区  ›  Raj

如何在迭代哈希映射时获得ConcurrentModificationException?

  •  2
  • Raj  · 技术社区  · 11 年前

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

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

    由于哈希映射是故障快速的。

    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 回复  |  直到 11 年前
        1
  •  3
  •   T.J. Crowder    11 年前

    碰巧由 HashMap 代码无法检测到这种情况。的代码 哈希图 的迭代器 hasNext 在Oracle的JDK7中是:

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

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

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

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

    哪一个 检查一下。

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

    1. 您创建 哈希图 .
    2. 您可以在其中添加一个条目。
    3. 您开始一个迭代。
    4. 有下一个 是真的,所以你进入循环的主体。
    5. 元素来自 下一个 ; 此时,迭代器会记住其内部数据成员上的下一个元素应该是什么(名称混乱 下一个 ),在这种情况下,由于地图中没有下一个元素 下一个 数据成员设置为 null ,意味着迭代已经完成。
    6. 您将添加到地图中。
    7. 您的代码调用 有下一个 ,可以看到 下一个 数据成员为 无效的 和返回 false .

    如果在开始循环之前,映射中有两个元素,而不是一个,则会得到异常(来自 下一个 ).

    我之前曾认为这是一个bug,或者几乎是一个bug,但它是一个相当模糊的领域,其他人也相当合理地认为它不是。文档没有具体说明 Iterator<E> 将抛出异常,只是它将被抛出。文件还说,这只是在“尽最大努力”的基础上进行的,并不能保证。

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

        2
  •  0
  •   cyberz    11 年前

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

    从HashMap的javadoc中:

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

        3
  •  0
  •   Sujoy    11 年前

    试试这个:

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