1. 程式人生 > >HashCode()和equals()方法詳解

HashCode()和equals()方法詳解

這兩個方法的問題一直困擾了我很久。不清楚他們各自的用處和實現,記得在實習的時候遇到過一個問題:將自定義的物件放在一個set集合中,目的是篩選出不重複的物件集合,但是結果卻是錯誤的。比如說有10個物件,但是其中有兩個是相等的,那麼期望的set的大小應該是9,但是最終結果卻是10。讓我很奇怪,後來帶我的師傅說了一句話:需要重寫類中的hashCode()和equals()方法。我還是一頭霧水,最近才研究了一個這兩個方法,總結一下。

 

Java中HashCode()和equals()方法是Object中的,所以每個物件都有這兩個方法。 equals()和hashCode()方法是用來在同一類中做比較用的,尤其是在容器裡如set存放同一類物件時用來判斷放入的物件是否重複。

 

首先我們需要搞清楚一個問題:

equals()相等的兩個物件,hashCode()一定相等;equals()不相等的兩個物件,hashCode()可能相等也可能不等。反過來:hashCode()不相等,他們的hashCode()一定不相等;hashCode()相等,equals()不一定相等。舉一個例子比較形象,hashCode()就像字典裡的索引,equals()就像字典中同一個字下的不同的詞語,“自己”和“自發”這兩個詞的索引“自”是一樣的(hashCode),但是卻是不相等的(equals)。

 

在Object類中,hashCode()是本地方法,返回的是物件的地址值,而Object類中equals()返回的也是地址值。

 

值得一提的是,hash演算法對於查詢元素提高了效率。如果想要在一個集合中找到你想要的元素,通常的做法是遍歷整個集合,取出每個物件比較是否是你想要的,找到則返回。但是當集合足夠大時,這種做法的平均時間是很長的(效能低)。有人發明了一種雜湊演算法,這種方法將集合分成幾個若干的儲存區域,每個物件可以計算出一個雜湊碼,可以將雜湊碼分組(根據相同的函式計算雜湊碼),每個組對應一個儲存空間,那麼查詢一個物件的時候,先計算他的雜湊碼就可以先確定他是在那個儲存區域的,然後只需要在這個區域遍歷比較即可;插入一個元素的時候,也先計算他的雜湊碼,確定儲存區域,再在該儲存區域新增該元素。Object類中定義的hashCode()方法就是返回每個物件的雜湊碼,當從hashSet集合中查詢一個物件時,java系統首先物件的hashCode()獲取該物件的雜湊碼錶,然後根據雜湊碼找到對應的儲存區域,最後取得該儲存區域的每個元素與該物件進行equals()比較。

可以看出,HashCode對於查詢元素有著較高的效能,但是插入元素時,效率相對低些。 因為向HashSet集合中新增一個物件時,要先計算出物件的雜湊碼和根據這個雜湊碼確定物件在集合中的存放位置為了保證一個類的例項物件能在HashSet正常儲存,要求這個類的兩個例項物件用equals()方法比較的結果相等時,他們的雜湊碼也必須相等;也就是說,如果obj1.equals(obj2)的結果為true,那麼以下表達式的結果也要為true:

obj1.hashCode() == obj2.hashCode()

換句話說:當我們重寫一個物件的equals方法,就必須重寫他的hashCode方法,不過不重寫他的hashCode方法的話,Object物件中的hashCode方法始終返回的是一個物件的hash地址,而這個地址是永遠不相等的。所以這時候即使是重寫了equals方法,也不會有特定的效果的,因為hashCode方法如果都不想等的話,就不會呼叫equals方法進行比較了,所以沒有意義了。

 

看到這裡就應該明白剛開始的問題,自定義的物件的hashCode()返回的是每個物件的地址值,永遠不會相等,因此在放入hashSet中時,hashCode()不等,就不會比較equlas()方法了,因此都不會被放入set中。 Object類中的hashCode()方法不能滿足物件被存入到HashSet中的要求,因為它的返回值是通過物件的記憶體地址推算出來的,同一個物件在程式執行期間的任何時候返回的雜湊值都是始終不變的,所以,只要是兩個不同的例項物件,即使他們的equals方法比較結果相等,他們預設的hashCode方法的返回值是不同的。