【问题标题】:What basic operations on a Map are permitted while iterating over it?在对 Map 进行迭代时,允许对其进行哪些基本操作?
【发布时间】:2010-10-04 04:24:09
【问题描述】:

假设我在 Java 中迭代一个 Map...我不清楚在迭代它的过程中我可以对该 Map 做什么。我想我对迭代器接口删除方法的 Javadoc 中的这个警告很困惑:

[...] 如果在迭代过程中通过调用此方法以外的任何方式修改了底层集合,则迭代器的行为是未指定的。

我确信我可以毫无问题地调用 remove 方法。但是在遍历 Map 集合时,我可以:

  1. 使用 Map 类 put 方法更改与键关联的值(使用现有键放置)?

  2. 使用 Map 类 put 方法添加新条目(使用新键放置)?

  3. 使用 Map 类的 remove 方法删除条目?

我的猜测是我可能可以安全地执行 #1(放入现有密钥),但不能安全地执行 #2 或 #3。

提前感谢您对此的任何澄清。

【问题讨论】:

    标签: java iterator


    【解决方案1】:

    您可以使用Iterator.remove(),如果使用(Map.Entry 的)entrySet 迭代器,您可以使用Map.Entry.setValue()。其他任何事情,所有赌注都被取消 - 您不应该直接更改地图,有些地图会 不允许上述任何一种或两种方法。

    具体来说,您的 (1)、(2) 和 (3) 是不允许

    可能可以通过Map 对象设置现有键的值,但Set.iterator() 文档明确排除了这一点,它将是特定于实现的:

    如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作,或通过迭代器返回的映射条目上的 setValue 操作除外),则迭代的结果是未定义的。 (强调)

    【讨论】:

    • 我确认您的建议很好。 Map.Entry.setValue() 就像一个魅力,Iterator.remove() 也是。
    • 为了完整起见,如何通过 public mutator 修改迭代器当前值? Map 无法查看每个条目的方法调用,对吧?
    【解决方案2】:

    如果您查看 HashMap 类,您会看到一个名为“modCount”的字段。这就是地图在迭代期间知道何时被修改的方式。任何在迭代时增加 modCount 的方法都会导致它抛出 ConcurrentModificationException。

    也就是说,如果键已经存在,您可以将值放入映射中,从而使用新值有效地更新条目:

     Map<String, Object> test = new HashMap<String, Object>();
     test.put("test", 1);
    
     for(String key : test.keySet())
     {
         test.put(key, 2); // this works!
     }
    
     System.out.println(test); // will print "test->2"
    

    当您询问是否可以“安全地”执行这些操作时,您不必太担心,因为 HashMap 旨在在遇到此类问题时立即抛出 ConcurrentModificationException。这些操作会很快失败;他们不会让地图处于糟糕的状态。

    【讨论】:

    • 这些方法尝试快速失败,但由于内存并发问题,如果修改是在另一个线程中完成的,它们可能并不总是。始终按照 API 文档进行编程;永远不要违反它,因为“我尝试过它并且它有效”,因为它可能只适用于您的 (a) JVM,或 (b) 硬件,或 (c) 特定配置,但不适用于其他配置。
    【解决方案3】:

    没有全局答案。地图界面让用户选择。不幸的是,我认为 jdk 中的所有实现都使用 fail-fast 实现(这里是 fail-fast 的定义,正如它在HashMap Javadoc 中所述):

    所有这些返回的迭代器 类的“集合视图方法”是 快速失败:如果地图在结构上 修改后的任何时间 以任何方式创建迭代器,除了 通过迭代器自己的remove 方法,迭代器将抛出一个 并发修改异常。因此, 面对并发 修改,迭代器失败 快速而干净,而不是 冒着任意的、非确定性的风险 未确定时间的行为 未来。

    【讨论】:

      【解决方案4】:

      一般来说,如果你想在迭代 Map 时改变它,你应该使用迭代器的方法之一。我还没有实际测试过#1 是否会起作用,但其他的肯定不会。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-08-11
        • 1970-01-01
        • 2016-10-08
        • 1970-01-01
        • 1970-01-01
        • 2021-09-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多