【问题标题】:While iterating over a map, for a value, it gets printed though it is already removed在迭代地图时,对于一个值,它会被打印出来,尽管它已经被删除了
【发布时间】:2016-05-11 11:13:19
【问题描述】:

当我迭代映射 (HashMap,HashTable,ConcurrentHashMap) 时,当我尝试删除特定条目时,在迭代中它仍然使用空值打印它。但并不是每一个条目都会发生这种情况。

  public class Test {
    public static void main(String[] args) {
      ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
      map.put("Pujan", "pujan");
      map.put("Swati", "swati");
      map.put("Manish", "manish");
      map.put("Jayant", "pujan");
      System.out.println(map);

      for (String string : map.keySet()) {
        System.out.println(string+","+map.get(string));
        map.remove("Manish");
      }
      System.out.println(map);
    }
  }

输出:

{Jayant=pujan, Swati=swati, Manish=manish, Pujan=pujan}
Jayant,pujan
Swati,swati
Pujan,pujan
{Jayant=pujan, Swati=swati, Pujan=pujan}

??第二个场景

//map.remove("Manish");
map.remove("Swati");

输出:

{Jayant=pujan, Swati=swati, Manish=manish, Pujan=pujan}
Jayant,pujan
**Swati,null**
Manish,manish
Pujan,pujan
{Jayant=pujan, Manish=manish, Pujan=pujan}

【问题讨论】:

  • 为什么map.removefor 循环内?
  • 你不应该在迭代它时使用Map.remove。大多数时候你会得到一个并发修改异常,如果没有,你会得到不可预知的结果。
  • @RealSkeptic - 他正在使用 ConcurrentHashMap!!
  • @StephenC 该问题还询问了HashMapHashTable - 这将在哪里失败 - 对于ConcurrentHashMap,将产生不可预测的结果。

标签: java collections


【解决方案1】:

查看 keySet() 的 JavaDocs(假设是 Java8)[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#keySet--][1]

返回此映射中包含的键的 Set 视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。该集合支持元素移除,即通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作从此映射中移除相应的映射。它不支持 add 或 addAll 操作。

视图的迭代器和拆分器是弱一致的。

链接到:

保证它们在构造时恰好遍历元素一次,并且可能(但不保证)反映构造后的任何修改。

这就是你所看到的。

控制 for 循环的迭代器不保证在您构建视图后反映更改。

视图始终由 4 个原始条目构成。现在,在带有“Manish”的示例中,恰好迭代器确实反映了修改,因此字符串永远不会将 Manish 作为值,并且您永远不会这样做map.get("Manish")。唉,对于 Swati,迭代器并没有反映修改,尽管数据始终是一致的,但它仍然为您提供“Swati”作为第二个元素:map.get("Swati") 确实反映了更改,根据 JavaDocs。

我还没有阅读 ConcurrentHashMap.MapEntry 中的代码(你应该),但是“Swati”而不是“Manish”发生这种情况的原因很可能如下。

  • 迭代器已创建。状态:next = "Jayan", nodes = ["Swati", "Manish", "Pujan"]
  • 迭代器.next()。返回“杰扬”。状态:next = "Swati", nodes = ["Manish", "Pujan"]

此时,执行 iterator.next() 将返回 current = "Swati"(如果元素已从地图中消失,这将与随后的 map.get() 一起显示)

如果您删除“Manish”(或“Pujan”),迭代器可以更新nodes。但是,如果您删除“Swati”,则为时已晚,因为 current 已经加载了该密钥。

【讨论】:

    【解决方案2】:

    ConcurrentHashMap 中的keySet 方法返回一个KeySetView,它有一个weakly consistent iterator,换句话说,它返回的iterator 是该集合在创建时的快照。

    来自ConcurrentHashMap#keySet() Java 文档

    视图的迭代器和拆分器是弱一致的

    但是当您调用 map.get 时,更改是可见的,这意味着 map.get 将返回 null,因为您要求的键不存在

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-03
      • 2012-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多