11

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

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

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

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

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

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

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

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

4

4 回答 4

13

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

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

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

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

于 2009-01-29T07:46:29.257 回答
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。这些操作会很快失败;他们不会让地图处于糟糕的状态。

于 2009-01-29T05:59:22.313 回答
2

没有全球性的答案。地图界面让用户选择。不幸的是,我认为 jdk 中的所有实现都使用了快速失败的实现(这是快速失败的定义,如HashMap Javadoc中所述):

所有此类的“集合视图方法”返回的迭代器都是快速失败的:如果在创建迭代器后的任何时间对映射进行结构修改,除了通过迭代器自己的 remove 方法之外,迭代器将抛出 ConcurrentModificationException . 因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

于 2009-01-29T07:58:18.140 回答
0

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

于 2009-01-29T05:22:20.703 回答