为什么map.vaules上的迭代器可以用来删除HashMap#Entry?

时间:2017-07-24 22:00:48

标签: java hashmap iterator

我正在尝试删除值为null的所有条目。代码是:

Map<String, String> map = new HashMap<>();
map.put("one", null);
map.put("two", null);
map.put("three", "THREE");

Iterator iterator = map.values().iterator();
while (iterator.hasNext())
{
    if (iterator.next() == null) {
        iterator.remove();
    }
}

for (Map.Entry<String, String> e : map.entrySet()) {
    System.out.println(e.getKey() + ":" + e.getValue());
}

我的问题是iterator绑定到map.values,为什么它可以删除整个条目?

2 个答案:

答案 0 :(得分:8)

这是可能的,因为Map#values会返回地图支持的值的视图

来自官方Java-Doc of Map#values

  

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

请注意,大多数地图实现扩展的AbstractMap类都有一个额外的字段transient volatile Collection<V> values,这正是您将在那里获得的。如您所见,集合在内部由Map使用,因此对它的更改也会反映在Map本身上。另见:Source code of AbstractMap

如果您想详细了解,请查看源代码中的AbstractMap#values方法。在那里,他们创建 values -collection作为在原始地图上运行的包装器。例如,其next方法会迭代地图的Entry<K, V>条目,但只返回Entry#getValue的值,依此类推。
如您所见,remove方法也会被传递给Entry<K, V>的迭代器,因此最终将在原始地图上执行删除。

答案 1 :(得分:2)

Zabuza已经给出了解释,但是因为有正确的方法来删除你的元素,我写了:

要删除值为Entry的空值,您可以使用Streams

map = map.entrySet()
         .stream()
         .filter(entry -> entry.getValue()!=null)
         .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));

单行map.entrySet().removeIf(e -> e.getValue()==null);

或者:map.values().removeIf(v -> v == null)