每個Java對象都有hashCode()和 equals()方法。許多類忽略(Override)這 些方法的預設實施,以在對象執行個體之間提供更深層次的語義可比性。在Java理念 和實踐這一部分,Java開發人員Brian Goetz向您介紹在建立Java類以有效和准 確定義hashCode()和equals()時應遵循的規則和指南。您可以在討論論壇與作者 和其它讀者一同探討您對本文的看法。(您還可以點擊本文頂部或底部的討論進 入論壇。)
雖然Java語言不直接支援關聯陣列 -- 可以使用任何對象作為一個索引的數 組 -- 但在根Object類中使用hashCode()方法明確表示期望廣泛使用HashMap(及 其前輩Hashtable)。理想情況下基於散列的容器提供有效插入和有效檢索;直接 在對象模式中支援散列可以促進基於散列的容器的開發和使用。
定義對象的相等性
Object類有兩種方法來推斷對象的標識:equals()和hashCode()。一般來說 ,如果您忽略了其中一種,您必須同時忽略這兩種,因為兩者之間有必須維持的 至關重要的關係。特殊情況是根據equals() 方法,如果兩個對象是相等的,它 們必須有相同的hashCode()值(儘管這通常不是真的)。
特定類的equals()的語義在Implementer的左側定義;定義對特定類來說 equals()意味著什麼是其設計工作的一部分。Object提供的預設實施簡單引用下 面等式:
public boolean equals(Object obj) { return (this == obj); }
在這種預設實施情況下,只有它們引用真正同一個對象時這兩個引用才是相 等的。同樣,Object提供的hashCode()的預設實施通過將對象的記憶體位址對映於 一個整數值來產生。由於在某些架構上,地址空間大於int值的範圍,兩個不同 的對象有相同的hashCode()是可能的。如果您忽略了hashCode(),您仍舊可以使 用System.identityHashCode()方法來接入這類預設值。
忽略 equals() -- 簡單一實例
預設情況下,equals()和hashCode()基於標識的實施是合理的,但對於某些 類來說,它們希望放寬等式的定義。例如,Integer類定義equals() 與下面類似 :
public boolean equals(Object obj) {
return (obj instanceof Integer
&& intvalue() == ((Integer) obj).intvalue());
}
在這個定義中,只有在包含相同的整數值的情況下這兩個Integer對象是相等 的。結合將不可修改的Integer,這使得使用Integer作為HashMap中的關鍵字是 切實可行的。這種基於值的Equal方法可以由Java類庫中的所有原始封裝類使用 ,如Integer、Float、Character和Boolean以及String(如果兩個String對象包 含相同順序的字元,那它們是相等的)。由於這些類都是不可修改的並且可以實 施hashCode()和equals(),它們都可以做為很好的散列關鍵字。
為什麼忽略 equals()和hashCode()?
如果Integer不忽略equals() 和 hashCode()情況又將如何?如果我們從未在 HashMap或其它基於散列的集合中使用Integer作為關鍵字的話,什麼也不會發生 。但是,如果我們在HashMap中使用這類Integer對象作為關鍵字,我們將不能夠 可靠地檢索相關的值,除非我們在get()調用中使用與put()調用中極其類似的 Integer執行個體。這要求確保在我們的整個程式中,只能使用對應於特定整數值的 Integer對象的一個執行個體。不用說,這種方法極不方便而且錯誤頻頻。
Object的interface contract要求如果根據 equals()兩個對象是相等的,那 麼它們必須有相同的hashCode()值。當其識別能力整個包含在equals()中時,為 什麼我們的根對象類需要hashCode()?hashCode()方法純粹用於提高效率。Java 平台設計人員預計到了典型Java應用程式中基於散列的集合類(Collection Class)的重要性--如Hashtable、HashMap和HashSet,並且使用equals()與許多 對象進行比較在計算方面非常昂貴。使所有Java對象都能夠支援 hashCode()並 結合使用基於散列的集合,可以實現有效儲存和檢索。
實施equals()和hashCode()的需求
實施equals()和 hashCode()有一些限制,Object檔案中列舉出了這些限制。 特別是equals()方法必須顯示以下屬性:
Symmetry:兩個引用,a和 b,a.equals(b) if and only if b.equals(a)
Reflexivity:所有非Null 參考, a.equals(a)
Transitivity:If a.equals(b) and b.equals(c), then a.equals(c)
Consistency with hashCode():兩個相等的對象必須有相同的hashCode()值
Object的規範中並沒有明確要求equals()和 hashCode() 必須一致 -- 它們 的結果在隨後的調用中將是相同的,假設“不改變對象相等性比較中使用的任何 資訊。”這聽起來象“計算的結果將不改變,除非實際情況如此。”這一模糊聲 明通常解釋為相等性和散列值計算應是對象的可確定性功能,而不是其它。