2009-07-02 140 views
10

我正確的假設,如果你有一個對象包含在一個Java集<>(或作爲一個關鍵在地圖<>這個問題),任何領域是用於確定身份或關係(通過hashCode()equals()compareTo()等)不能在沒有對採集造成不確定的行爲進行操作改變了嗎? (編輯:作爲暗示在this other question可變字段的對象在Java集

(換句話說,這些領域要麼是不可變的,或者如果您需要將對象從集合中刪除,然後改,然後重新插入。)

的我想問的原因是我正在閱讀Hibernate Annotations reference guide,它有一個例子,其中HashSet<Toy>Toy類的字段nameserial是可變的,也用於計算hashCode() ......紅旗在我的頭和我只是想確保我理解它的含義。

回答

7

Set的Javadoc說

注:如果 可變對象用作設置 元素大,一定要小心。一組的行爲不是 指定某個對象的值是 在影響 equals比較而對象是 集合中的元素的方式改變。這種禁止的特殊情況是 ,它不是 允許對一組含有 本身作爲一個元素。

這只是意味着你可以在一個集合中使用可變對象,甚至可以改變它們。您只需確保更改不會影響Set找到項目的方式。對於HashSet,這將不需要更改用於計算hashCode()的字段。

3

這是正確的,它可能會導致一些問題,定位映射條目。正式的行爲是未定義的,所以如果你將它添加到哈希集或哈希表中的鍵,你不應該改變它。

1

是的,那會導致不好的事情發生。

// Given that the Toy class has a mutable field called 'name' which is used 
// in equals() and hashCode(): 
Set<Toy> toys = new HashSet<Toy>(); 
Toy toy = new Toy("Fire engine", ToyType.WHEELED_VEHICLE, Color.RED); 
toys.add(toy); 
System.out.println(toys.contains(toy)); // true 
toy.setName("Fast truck"); 
System.out.println(toys.contains(toy)); // false 
+0

等等,我只是意識到這是一個非常糟糕的例子。由於我仍然持有該引用,所以最後的contains()實際上將返回true。 HashMaps是另一回事,但它幾乎是在三天的週末上放棄時間,我不想挖掘一個例子。 – 2009-07-02 21:33:30

0

在HashSet/HashMap中,您可以可以變異包含的對象以更改compareTo()的結果操作 - 相對比較不用於查找對象。但是它在TreeSet/TreeMap中是致命的。

也可以發生變異是一個IdentityHashMap中的對象 - 無非對象標識等用於定位的內容。

即使你可以做這些事情與這些資格,他們使你的代碼更加脆弱。如果有人希望稍後更改爲TreeSet,或將該可變字段添加到hashCode /等於測試中,該怎麼辦?