2012-07-05 53 views
1

我想,反覆HashMap並改寫一些元素與其它地圖,但是我有以下問題:的HashMap的keySet變化不會反映在地圖

@Test 
public void test() { 
    Map<SubClass, String> map = new HashMap<SubClass,String>(); 
    Map<SubClass, String> anotherMap = new HashMap<SubClass,String>(); 
    map.put(new SubClass(), "10"); 

    for(SubClass i : map.keySet()) { 
     System.out.println(i); // initial (because toString is implemented) 
     System.out.println(map.get(i)); // 10 
     // here it's ok... 

     i.name="another"; 

     System.out.println(i); // another 
     System.out.println(map.get(i)); // null! 
     // but here it occurs that map.get(i) returns null! 

     anotherMap.put(i, map.get(i)); 
    } 
    for(SubClass i : anotherMap.keySet()) { 
     System.out.println(i); // another 
     System.out.println(map.get(i)); // null! 
    } 
} 
// SubClass has String name; and hashCode and equals implemented 

據的Javadoc:

java.util.Map.keySet()

返回此映射中包含的鍵的Set視圖。該組由地圖支持,因此對地圖的更改反映在該組中,反之亦然。如果在對集合的迭代處於 進度(除了通過迭代器自己的刪除操作)上迭代迭代後修改映射,則迭代結果爲 未定義。該集合支持元素刪除,該操作通過Iterator.remove,Set.remove,removeAll,retainAll和clear操作從映射中刪除相應的映射,即 。它不支持 add或addAll操作。

它說「地圖的變化反映在集合中,反之亦然」。那麼爲什麼它的行爲是這樣,最重要的:我如何克服它,使兩個圖只包含修改後的鍵和非空值?

UPDATE: 我的朋友做了關於Java 1.5.0.19(我有1.7.0_03,同樣發生在1.5.0_21)該項測試,並得到了正確的輸出:

initial 
10 
another 
10 

UPDATE2: 哦,他沒有實現的hashCode /等於,所以第一次更新是無關緊要

+2

你正在改變的關鍵...這永遠不會工作,因爲你的第二個呼叫map.get(我)是基於比第一個 – Fido 2012-07-05 15:29:52

+0

這樣一個不同的密鑰是什麼意思javadoc中的這一部分:「更改地圖反映在集合中,反之亦然「? – 2012-07-05 15:31:22

+0

集合中的變化與集合中元素的變化不同 – Fido 2012-07-05 15:32:31

回答

4

你修改關鍵,而不是地圖Map<K,V>無法檢測到您已更改其中的對象。要使地圖「看到」更改,您需要撥打remove(originalKey),更改密鑰,然後致電put(modifiedKey,object)

修改地圖可以調用clear,put,putAllremove

+0

請查看我的更新。那爲什麼它對我的朋友有效? – 2012-07-05 15:37:30

+1

也許工作對你的朋友爲這個特定的操作,但如果'name'是重點對象的'equals'或'hashCode'實施的一部分,該地圖的行爲是_undefined_。一般來說,這意味着在實踐中,地圖的行爲是不可預測的:出現在'entrySet'中的條目不會與'map.get(key)'一起出現;您不能刪除以這種方式損壞的條目。地圖是_corrupted._ – 2012-07-05 16:23:21

+0

@AdamPierzchała我不能說沒有看到你的朋友代碼。 – 2012-07-05 16:33:27

0

我相信i.name = "another"正在改變SubClass i對象的哈希碼,這就是爲什麼你找回null值。如果Subclasshashcode函數不使用name,代碼將工作。

所以,當你調用anotherMap.put(i, map.get(i));你真的叫anotherMap.put(i, null);

HashMap的目的是爲O執行查找(1)時間。這就是爲什麼它需要SubClass i的哈希碼,並且不會更新它。你的建議將需要一個懶惰的HashMap,但這將花費O(n)時間並擊敗目的。

0

嘗試的東西,如:

for(SubClass i : map.keySet()) { 
    Object old = map.get(i); 

    i.name="another"; 

    anotherMap.put(i, old); 
} 
-1

我回答我自己,因爲我的朋友之前有比任何人都更好的解決方案在這裏:

for (Map.Entry<SubClass, String> entry: map.entrySet()) { 
    System.out.println(entry.getKey().name); 
    System.out.println(entry.getValue()); 

    entry.getKey().name = "another"; 

    System.out.println(entry.getKey().name); 
    System.out.println(entry.getValue()); 
} 

這將工作:)

+1

我懷疑這會起作用。如果'SubClass.equals()'或'hashCode'取決於'name',那麼這一切都將做的是腐敗的地圖,使自己的行爲完全不可預測的。您應該___不要修改映射中的關鍵對象,特別是不要影響它們的equals()或hashCode()。 – 2012-07-05 16:18:17

+0

你檢查過了嗎?我檢查了它並且工作(對於這個簡單的例子),爲什麼/何時不起作用? – 2012-07-09 07:46:38

+0

好的,我讀過@Kenster的回答 - 在javadocs中沒有注意到這一點。我會改變實施。 – 2012-07-09 07:52:28

3

java.util.Map javadoc

注意:如果可變對象用作映射鍵,則必須非常小心。如果對象的值以影響等於比較的方式更改,而對象是地圖中的關鍵字,則不會指定地圖的行爲。

換句話說,如果您更改鍵對象的方式會影響鍵在地圖中的索引方式,則無需正確地使用地圖。