Hashmap.keySet(),foreach和remove

Sam*_*urn 14 java foreach iterator hashmap

我知道使用java的"foreach"从列表中删除通常是一个很大的禁忌,并且应该使用iterator.remove().但是如果我循环遍历HashMap的keySet(),那么remove()是否安全?像这样:

for(String key : map.keySet()) {
  Node n = map.get(key).optimize();
  if(n == null) {
   map.remove(key);
  } else {
   map.put(key, n);
  }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 18

编辑:

我没有注意到你没有真正添加到地图 - 你只是改变了条目中的值.在这种情况下,pstanton(编辑前1)解决方案几乎是正确的,但你应该调用setValue迭代器返回的条目,而不是调用map.put.(这是有可能的是map.put将工作,但我不相信这是保障-而文档状态entry.setValue .工作)

for (Iterator<Map.Entry<String, Node>> it = map.entrySet().iterator(); 
     it.hasNext();)
{
    Map.Entry<String, Node> entry = it.next();
    Node n = entry.getValue().optimize();
    if(n == null) 
    {
        it.remove();
    }
    else
    {
        entry.setValue(n);
    }
}
Run Code Online (Sandbox Code Playgroud)

(遗憾的是entry没有remove方法,否则你仍然可以使用增强的for循环语法,使它变得不那么笨重.)

老答案

(我在这里留下了更常见的情况,你只想进行任意修改.)

不 - 您既不应该添加到地图中,也不应该直接从中删除.返回的集合HashSet.keySet()是对键的视图,而不是快照.

可以通过迭代器删除,但这需要您显式使用迭代器而不是通过增强的for循环.

一个简单的选择是从原始创建一个新集:

for (String key : new HashSet<String>(map.keySet())) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

此时你没事,因为你没有对套装做任何改变.

编辑:是的,你绝对可以通过键集迭代器删除元素.来自以下文档HashMap.keySet():

该集支持元素删除,它通过Iterator.remove,Set.remove,removeAll,retainAll和clear操作从地图中删除相应的映射.它不支持add或addAll操作.

这甚至在Map界面本身中指定.


1我决定编辑我的答案,而不仅仅是评论psanton的答案,因为我认为我得到的类似但不同的情况的额外信息对于值得这个答案保持足够有用.


pst*_*ton 12

你应该使用条目集:

for(Iterator<Map.Entry<String, Node>> it = map.entrySet().iterator(); it.hasNext();)
{
      Map.Entry<String, Node> entry = it.next();
      Node n = entry.getValue().optimize();
      if(n == null) 
          it.remove();
      else
          entry.setValue(n);
}
Run Code Online (Sandbox Code Playgroud)

编辑固定代码