簡體   English   中英

可變集合是否應該重寫equals和hashCode?

[英]Should mutable collections override equals and hashCode?

我只是想知道為可變集合重寫equalshashCode是否是個好主意。 這意味着如果我將這樣的集合插入HashSet然后修改集合, HashSet將無法再找到集合。 這是否意味着只有不可變集合才能覆蓋equalshashCode ,或者這是一個令人討厭的Java程序員只是生活在一起?

如果您的類應該像值類型一樣,則應該重寫equalshashCode 這通常不是收藏品的情況。

(我的Java經驗並不多。這個答案基於C#。)

深度和淺等的問題比Java大; 所有面向對象的語言都必​​須關注它。

添加到集合的對象應該重寫equals和hash代碼,但是集合接口的抽象實現中內置的默認行為足以滿足集合本身的要求。

它與任何可變類都相同。 當您將實例插入HashSet然后調用變異方法時,您將遇到麻煩。 所以,我的答案是:是的,如果有用的話。

在將其添加到HashSet之前,您當然可以為Collection使用不可變的Wrapper。

我認為更大的問題是如果有人試圖將一個FredCollection實例添加到Set兩次會發生什么。

FredCollection c = ...
set.add(c);
set.add(c);

在此之后, setsize()應該是2還是1

你是否需要測試兩個不同FredCollection實例的“相等性”? 我認為這個問題的答案在確定你的equals() / hashcode()行為時比其他任何事情更重要。

這不僅僅是集合的問題,而是一般的可變對象(另一個例子: Point2D )。 是的,這是Java程序員最終學會考慮的一個潛在問題。

您不應該重寫equals和hashCode,以便它們反映可變成員。

更多是我個人的觀點。 我認為哈希碼和等號是不應該用於實現業務邏輯的技術術語。 想象一下:你有兩個對象(不僅是集合)並詢問它們是否相等,然后有兩種不同的方法來回答它們:

  • 技術:該是平等的,如果它們表示相同的對象,這是從不同的相同的對象(如果你認為代理,serilization,遠程的東西...的)
  • bussines邏輯:如果看起來相同(相同的屬性)它們是相等的 - 這里重要的是,即使在一個應用程序中,即使對於同一個類,也沒有平等的聖潔定義。 (示例問題:兩塊石頭何時相等?))

但是因為equals是由技術人員(HashMap)使用的,所以你應該以技術方式實現它,並通過別的東西構建與業務邏輯相關的東西(比如比較器接口)。 對於您的集合,它意味着:不要覆蓋equals和hashCode(以打破技術合同的方式:

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

(java doc of Map))。

equalshashCode一個基本困難是有兩種邏輯方式可以定義等價關系; 一個類的一些消費者會想要一個定義,而同一個類的其他消費者會想要另一個。

我將如下定義兩個等價關系:

  • 如果使用對Y的引用覆蓋X將不會改變X或Y的任何成員的當前或未來行為,則兩個對象引用X和Y完全等效。

  • 如果在一個程序中沒有持久保存從與身份相關的哈希函數返回的值,那么兩個對象引用X和Y具有等效狀態,將所有對X的引用交換為所有對Y的引用將使程序狀態保持不變。

請注意,第二個定義主要與常見場景相關,其中兩個東西包含對某些可變類型(例如數組)的對象的引用,但可以確定,至少在某個特定的感興趣的時間范圍內,這些對象不是會接觸到任何可能會改變它們的東西。 在這種情況下,如果“持有者”對象在所有其他方面都是等同的,則它們的等同性應取決於它們所持有的對象是否滿足上述等價的第二定義。

請注意,第二個定義並不涉及對象狀態可能如何變化的任何細節。 進一步注意,對於等價定義,不可變對象可以將具有相同內容的不同對象報告為相等或不相等(如果X和Y不同的唯一方式是X.Equals(X)在X.Equals(Y)時報告為真。 )報告錯誤,這將是一個區別,但讓這些對象使用引用標識作為第一個等價關系和其他方面的等同性可能是最有用的。

不幸的是,因為Java只提供了一對等價定義類,所以類設計者必須猜測等價的哪個定義與該類的消費者最相關。 雖然有一個充分的論據支持總是使用第一個,但第二個通常更實用。 第二個問題的最大問題是類無法知道使用該類的代碼何時需要第一個等價關系。

equals用於添加/刪除集合中的元素,如CopyOnWriteArraySet,HashSet,如果hashCode對於兩個不同的對象相等,則等於需要是對稱的,即如果B.equals(C)返回true,則C.equals(B)應該返回同樣的結果。 否則,您在這些XXXSets上添加/刪除的行為會讓人感到困惑。 檢查CopyOnWriteArraySet.add的覆蓋等於並刪除有關如何不正確覆蓋equals影響對集合的添加/刪除操作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM