Java Hashmap.keySet()、foreach和remove

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2026104/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 02:45:03  来源:igfitidea点击:

Hashmap.keySet(), foreach, and remove

javaiteratorforeachhashmap

提问by Sam Washburn

I know that it's typically a big no-no to remove from a list using java's "foreach" and that one should use iterator.remove(). But is it safe to remove() if I'm looping over a HashMap's keySet()? Like this:

我知道使用 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);
  }
}

采纳答案by Jon Skeet

EDIT:

编辑:

I hadn't noticed that you weren't really addingto the map - you were just changing the value within the entry. In this case, pstanton's (pre-edit1) solution is nearlyright, but you should call setValueon the entry returned by the iterator, rather than calling map.put. (It's possiblethat map.putwill work, but I don't believe it's guaranteed - whereas the docs state that entry.setValuewillwork.)

我没有注意到您并没有真正添加到地图中 - 您只是更改了条目中的值。在这种情况下,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);
    }
}

(It's a shame that entrydoesn't have a removemethod, otherwise you could still use the enhanced for loop syntax, making it somewhat less clunky.)

(遗憾的是entry没有remove方法,否则您仍然可以使用增强的 for 循环语法,使其不那么笨拙。)

Old answer

旧答案

(I've left this here for the more general case where you just want to make arbitrary modifications.)

(我把这个留在这里是为了更一般的情况,你只想进行任意修改。)

No - you should neither add to the map nor remove from it directly. The set returned by HashSet.keySet()is a view onto the keys, not a snapshot.

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

You canremove via the iterator, although that requires that you use the iterator explicitly instead of via an enhanced for loop.

可以通过迭代器删除,尽管这要求您显式使用迭代器而不是通过增强的 for 循环。

One simple option is to create a new set from the original:

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

for (String key : new HashSet<String>(map.keySet())) {
    ...
}

At this point you're fine, because you're not making any changes to the set.

在这一点上,您很好,因为您没有对集合进行任何更改。

EDIT: Yes, you can definitely remove elements via the key set iterator. From the docs for HashMap.keySet():

编辑:是的,您绝对可以通过键集迭代器删除元素。从文档中HashMap.keySet()

The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.

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

This is even specified within the Mapinterface itself.

这甚至在Map接口本身中指定。



1I decided to edit my answer rather than just commenting on psanton's, as I figured the extra information I'd got for similar-but-distinct situations was sufficiently useful to merit this answer staying.

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

回答by pstanton

you should use the entry set:

您应该使用条目集:

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

EDIT fixed code

编辑固定代码