1. 程式人生 > >為什麼要重寫equals() 和 hashcode() 方法

為什麼要重寫equals() 和 hashcode() 方法

重寫equals() 是為了保證比如new ArrayList().contains(Object)的基於equals() 做比較的可用性

重寫hashcode() 是為了保證比如new hashMap().put(Object)的基於hashcode() 做key值的可用性

 

Java中的集合(Collection)有兩類,一類是List,再有一類是Set。 
前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。 
那麼我們怎麼判斷兩個元素是否重複呢? 這就是Object.equals方法了。

通常想查詢一個集合中是否包含某個物件,就是逐一取出每個元素與要查詢的元素進行比較,當發現某個元素與要查詢的物件進行equals方法比較的結果相等時,則停止繼續查詢並返回肯定的資訊,否則返回否定的資訊,如果一個集合中有很多元素譬如成千上萬的元素,並且沒有包含要查詢的物件時,則意味著你的程式需要從該集合中取出成千上萬個元素進行逐一比較才能得到結論,於是,有人就發明了一種雜湊演算法來提高從集合中查詢元素的效率,這種方式將集合分成若干個儲存區域,每個物件可以計算出一個雜湊碼,可以將雜湊碼分組,每組分別對應某個儲存區域,根據一個物件的雜湊碼就可以確定該物件應該儲存的那個區域.

hashCode方法可以這樣理解:它返回的就是根據物件的記憶體地址換算出的一個值。這樣一來,當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。

談到hashcode()和equals()就不能不說到hashset,hashmap,hashtable中的使用,具體是怎樣呢,請看如下分析:

Hashset是繼承Set介面,Set介面又實現Collection介面,這是層次關係。那麼hashset是根據什麼原理來存取物件的呢? 
在hashset中不允許出現重複物件,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重複的呢?判斷兩個物件是否相等的規則是: 
.1),判斷兩個物件的hashCode是否相等 
如果不相等,認為兩個物件也不相等,完畢,如果相等,轉入2
.2),判斷兩個物件用equals運算是否相等 
如果不相等,認為兩個物件也不相等 
如果相等,認為兩個物件相等(equals()是判斷兩個物件是否相等的關鍵) 

小結:
(1)只有類的例項物件要被採用雜湊演算法進行儲存和檢索時,這個類才需要按要求覆蓋hashCode方法,即使程式可能暫時不會用到當前類的hashCode方法,但是為它提供一個hashCode方法也不會有什麼不好,沒準以後什麼時候又用到這個方法了,所以,通常要求hashCode方法和equals方法一併被同時覆蓋。

(2)equals()相等的兩個物件,hashcode()一定相等;equals()不相等的兩個物件,卻並不能證明他們的hashcode()不相等。換句話說,equals()方法不相等的兩個物件,hashcode()有可能相等。反過來:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

提示:
(1)通常來說,一個類的兩個例項物件用equal方法比較的結果相等時,它們的雜湊碼也必須相等,但反之則不成立,即equals方法比較結果不相等的物件可以有相同的雜湊碼,或者說雜湊碼相同的兩個物件的equal方法比較的結果可以不等。

(2)當一個物件被儲存進hashset集合中以後,就不能修改這個物件中的那些參與計算雜湊值的欄位了,否則,物件修改後的雜湊值與最初儲存進hashset集合時的雜湊值就不同了,這種情況下,即使在contains方法使用該物件的當前引用作為的引數去hashset集合中檢索物件,也將返回找不到物件的結果,這也會導致無法從hashset集合中單獨刪除當前物件,從而造成記憶體洩露,所謂的記憶體洩露也就說有一個物件不再被使用,但它一直佔有記憶體空間,沒有被釋放。