2017-07-24 68 views
5

我試圖刪除其值爲null的所有條目。該代碼是:爲什麼map.vaules上的迭代器可以用來刪除HashMap#Entry?

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,爲什麼它可以刪除整個項目嗎?

+0

只需確認一下,您的代碼就可以工作,但您試圖瞭解原因。正確? – shmosel

+0

是的。我試圖找到根本原因。 – caisil

回答

8

這是可能的,因爲Map#values返回值爲的視圖,該值由地圖支持。

從官方Java-Doc of Map#values

返回包含在此映射中的值的Collection視圖。該集合由地圖支持,因此地圖的更改會反映在集合中,反之亦然。 [...]該集合支持元素刪除,通過Iterator.remove,Collection.remove,removeAll,retainAll和clear操作從地圖中刪除相應的映射。它不支持add或addAll操作。


注意,AbstractMap類,從最Map實現擴展,有一個額外的領域transient volatile Collection<V> values而這正是你會得到那裏。正如你所看到的,這個集合在內部被這個Map使用,因此它的變化也反映在這個Map本身上。另請參見:Source code of AbstractMap


如果您喜歡在細節中去,看看在源代碼中的AbstractMap#values方法。在那裏,他們創建 -collection作爲在原始地圖上運行的包裝。例如,其next方法在Map的條目Entry<K, V>上進行迭代,但只返回其值Entry#getValue等等。
另外,您可以看到,remove方法被傳遞給Entry<K, V>的迭代器,因此最終的移除將在原始映射上再次執行。

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)

+1

或'map.values()。removeIf(v - > v == null)'。但我認爲你錯過了這個問題的重點。 – shmosel

+0

@shmosel是我剛剛意識到^^ – azro

+0

@azro @shmosel,來自'Collection.removeIf'的代碼,我基本上認爲'removeIf'方法與我的示例代碼中使用的方法相同,都基於'iterator' 。 – caisil

相關問題