深入詳細的理解hashCode()和equals()之間的關係

首先,我們要知道標題所示兩個方法均是基於散列表和散列算法纔有其聯繫意義的!

而,java中散列表使用鏈表數組實現。

先來簡單敘述散列表:是一種可以快速查找特定對象的數據結構。散列表可以對每一個對象計算一個整數,成爲散列碼。而根據這個散列碼,通過一定的散列算法,可以迅速計算出對象的位置!

我們通過HashSet來簡單理解,散列表的實現機制!

當向hashSet中添加元素a的時候,首先調用其a.hashCode()方法獲得其散列碼,從而計算出其在數組中的位置,也就是其所在的鏈表位置。定位到某個具體鏈表後,接下來調用a.equals()方法用元素a的值和當前定位鏈表中的每一個元素進行比較,如果有相同元素,a將被視爲已經添加過,從而不再重複的添加,否則將a添加到當前鏈表表頭!

 需要注意的是,這裏所謂的相同的邏輯是,基於equals方法的比較邏輯!

好了,以上內容均爲引入具體內容所需要的知識!

進入正題:

問題引入:兩個對象的hashCode()相同,則equals一定返回爲true,對嗎?

答案是:否定的! 

因爲:類的hashcode方法和equals方法,是可以重寫的,故其放回值完全取決於其方法的處理邏輯!

那麼,該問題有了擴展問題:

1.既然方法是可以重寫的,那其默認不重寫的時候,是如何的呢?

答案是:這兩個方法均爲基類Object中的方法,在Object中均有其默認實現:

Object中的hashcode()方法,其值對應於類的內存地址!

Object中的equals()方法,其比較結果與“==”號一致,也就是判斷的是引用是否相等!

下面是這兩個方法的默認實現下的聯繫:

此處附上jdk官方所給的詳細說明:

下面的話來自JDK:

        hashCode         

        public int hashCode()返回該對象的哈希碼值。

        支持此方法是爲了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。        

hashCode 的常規協定是: 
        在 Java 應用程序執行期間,在對同一對象多次調用 hashCode 方法時,必須一致地返回相同的整數,前提是將對象進行 equals 比較時所用的信息沒有被修改。

        從某一應用程序的一次執行到同一應用程序的另一次執行,該整數無需保持一致。        

        如果根據 equals(Object) 方法,兩個對象是相等的,那麼對這兩個對象中的每個對象調用 hashCode 方法都必須生成相同的整數結果。        

        如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法不 要求一定生成不同的整數結果。但是,程序員應該意識到,爲不相等的對象生成不同整數結果可以提高哈希表的性能。        

        實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數。(這一般是通過將該對象的內部地址轉換成一個整數來實現的,但是 JavaTM 編程語言不需要這種實現技巧。) 

 

2.此時,我們就需要來討論另一個問題了,那就是兩個方法進行重寫的時候:

根據上述常規性協定的描述,我們瞭解到了兩個方法之間的內在聯繫!

那麼,重寫的時候,我們也要遵循上述所示的常規協定:

即:兩個對象用equals比較返回爲true,那麼對象的hashcode方法必須返回相同的結果!

即:a.equals(b) is true;  必須保證:a.hashCode() 和 b.hashCode()一致

所以,當我們重寫euqals方法的時候,一般都必須要重寫hashCode()方法來滿足這個協定!

而滿足這個協定的一般解決思路:

      在equals中進行比較的屬性,一定也要用到hashCode方法中去計算散列碼!

那麼,重寫這兩個方法的意義是什麼?

簡單來說:

重寫equals,是爲了保證相同的元素,無法被同時添加到散列表中!

很容易理解:有個員工類,編號屬性作爲其唯一區分的依據,那麼不重寫equals時,同時向散列表添加兩個不同的對象,但是其編號是相同的,問題不就出現了嗎?就相當於,添加了兩個一樣的員工,而散列表卻不知道!因爲此時不重寫equals,equals的判斷邏輯是==,判斷的是引用比較,很顯然,兩個對象的引用地址是不同的!所以,我們要重寫equals,將邏輯變爲去比較兩個對象的id屬性,以達到我們想要的判斷邏輯!

重寫hashcode,一般都是爲了使散列法具有高效的查找速度,以及滿足相應上述所說的常規協定!

那麼,問題又來了,爲什麼非得有這麼一個協定呢?

其實,很簡單,基礎來說!hashCode()方法所計算出的散列碼就是爲了去找到散列表中的某一條特定的鏈表,也就是數組位置,這個協定是爲了保證,equals判斷相等的兩個對象,不可以被分到兩個鏈表中去,從而失去了equals判斷的邏輯,也就直接導致了散列表中添加了兩個一樣的對象。

 

理解了上述內容,最後,也提出一般equals自身重寫所必需的滿足的硬性條件:

①自反性:a.equals(a) is true

②對稱性:a.equals(b) 和 b.equals(a) 結果一致

③傳遞性:a.equals(b) is true,b.equals(c) is true, so a.equals(c) is true

④持續性:多次調用a.equals(b)的返回結果應該一致

⑤對任意非空a:a.equals(null) is false


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章