天天看點

equals和hashcode差別

equals方法

Object類中預設的實作方式是  :   return this == obj  。那就是說,隻有this 和 obj引用同一個對象,才會傳回true。

而我們往往需要用equals來判斷 2個對象是否等價,而非驗證他們的唯一性。這樣我們在實作自己的類時,就要重寫equals.

按照約定,equals要滿足以下規則。

自反性:  x.equals(x) 一定是true

對null:  x.equals(null) 一定是false

對稱性:  x.equals(y)  和  y.equals(x)結果一緻

傳遞性:  a 和 b equals , b 和 c  equals,那麼 a 和 c也一定equals。

一緻性:  在某個運作時期間,2個對象的狀态的改變不會影響equals的決策結果,那麼,在這個運作時期間,無論調用多少次equals,都傳回相同的結果。

hashCode方法

hashCode()方法是從Object類中繼承過來的,它也用來鑒定兩個對象是否相等。Object類中的hashCode()方法傳回對象在記憶體中位址轉換成的一個int值,是以如果沒有重寫hashCode()方法,任何對象的hashCode()方法都是不相等的。

對象的散列碼是為了更好的支援基于哈希機制的Java集合類,例如 Hashtable, HashMap, HashSet 等。

關于hashCode方法,一緻的約定是:

在某個運作時期間,隻要對象的(字段的)變化不會影響equals方法的決策結果,那麼,在這個期間,無論調用多少次hashCode,都必須傳回同一個散列碼。

如果2個對象通過equals調用後傳回是true,那麼這個2個對象的hashCode方法也必須傳回同樣的int型散列碼如果2個對象通過equals傳回false,他們的hashCode傳回的值允許相同。(然而,程式員必須意識到,hashCode傳回獨一無二的散列碼,會讓存儲這個對象的hashtables更好地工作。)

equals和hashcode差別

一般來講,equals()方法是給使用者調用的,如果需要判斷兩個對象是否相等的,可以重寫equals()方法,然後在代碼中調用,這樣就可以判斷它們是否相等了。

對于hashCode()方法,使用者一般不會去調用它,例如在hashmap中,由于key是不可以重複的,它在判斷key是否重複時就判斷了hashCode()方法,而且也用到了equals()方法。此處“不可以重複”指的是equals()和hashCode()隻要有一個不等就可以了。是以,hashCode()方法相當于是一個對象的編碼,就好像檔案中的md5,它與equals()方法的不同之處就在于它傳回的是int型,比較起來不直覺。

hashCode()方法和equals()方法的關系如下

1.若兩個對象equals傳回true,則hashCode有必要也傳回相同的int數。

2.若兩個對象equals傳回false,則hashCode不一定傳回不同的int數,但為不相等的對象生成不同hashCode值可以提高哈希表的性能。

3..若兩個對象hashCode傳回相同int數,則equals不一定傳回true。

4.若兩個對象hashCode傳回不同int數,則equals一定傳回false。

5.一般在覆寫equals()方法的同時也要覆寫hashCode()方法,否則,就會違反Object.hashCode的通用約定,進而導緻該類無法與所有基于散列值(hash)集合類(HashMap、HashSet和Hashtable)結合在一起正常運作。

為什麼覆寫equals()方法的同時要覆寫hashCode()方法?

Object.hashCode的通用約定

1.在一個應用程式執行期間,如果一個對象的equals方法做比較所用到的資訊沒有被修改的話,那麼,對該對象調用hashCode方法多次,它必須始終如一地傳回 同一個整數。在同一個應用程式的多次執行過程中,這個整數可以不同,即這個應用程式這次執行傳回的整數與下一次執行傳回的整數可以不一緻。

2.如果兩個對象根據equals(Object)方法是相等的,那麼調用這兩個對象中任一個對象的hashCode方法必須産生同樣的整數結果。

3.如果兩個對象根據equals(Object)方法是不相等的,那麼調用這兩個對象中任一個對象的hashCode方法,不要求必須産生不同的整數結果。然而,程式員應該意識到這樣的事實,對于不相等的對象産生截然不同的整數結果,有可能提高散清單(hash table)的性能。以Java.lang.Object來了解, JVM每次new一個Object, 都會将Object丢到一個哈希表中去, 這樣的話,下次做Object的比較或者取這個對象的時候, 它會根據對象的hashcode再從Hash表中取這個對象。這樣做的目的是提高取對象的效率。

如果隻重寫了equals方法而沒有重寫hashCode方法的話,則會違反約定的第二條:相等的對象必須具有相等的散列碼(hashCode)。

同時對于HashSet和HashMap這些基于散列值(hash)實作的類。HashMap的底層處理機制是以數組的方法儲存放入的資料的(Node<K,V>[] table),其中的關鍵是數組下标的處理。數組的下标是根據傳入的元素hashCode方法的傳回值再和特定的值異或決定的。如果該數組位置上已經有放入的值了,且傳入的鍵值相等則不處理,若不相等則覆寫原來的值,如果數組位置沒有條目,則插入,并加入到相應的連結清單中。檢查鍵是否存在也是根據hashCode值來确定的。是以如果不重寫hashCode的話,可能導緻HashSet、HashMap不能正常的運作、

如果我們将某個自定義對象存到HashMap或者HashSet及其類似實作類中的時候,如果該對象的屬性參與了hashCode的計算,那麼就不能修改該對象參數hashCode計算的屬性了。有可能會移除不了元素,導緻記憶體洩漏。

看一個代碼片段:

equals和hashcode差別

運作這段代碼發現結果傳回的是null。

原因:未重寫hashcode,是以兩個new Apple("green")的hashcode值不同,無法得到對應的對象。

我們來看下HashMap中的get源碼進行了解:

equals和hashcode差別

1.new Object(),JVM根據這個對象的Hashcode值,放入到對應的Hash表對應的Key上,如果不同的對象确産生了相同的hash值,也就是發生了Hash key相同導緻沖突的情況,那麼就在這個Hash key的地方産生一個連結清單,将所有産生相同hashcode的對象放到這個單連結清單上去,串在一起。

2.比較兩個對象的時候,首先根據他們的hashcode去hash表中找他的對象,當兩個對象的hashcode相同,那麼就是說他們這兩個對象放在Hash表中的同一個key上,那麼他們一定在這個key上的連結清單上。那麼此時就隻能根據Object的equal方法來比較這個對象是否equal。當兩個對象的hashcode不同的話,肯定他們不能equals.

重寫hashCode時注意事項

重寫hashCode方法時除了上述一緻性約定,還有以下幾點需要注意:

(1)傳回的hash值是int型的,防止溢出。

(2)不同的對象傳回的hash值應該盡量不同。(為了hashMap等集合的效率問題)

(3)《Java程式設計思想》中提到一種情況

“設計hashCode()時最重要的因素就是:無論何時,對同一個對象調用hashCode()都應該産生同樣的值。如果在講一個對象用put()添加進HashMap時産生一個hashCdoe值,而用get()取出時卻産生了另一個hashCode值,那麼就無法擷取該對象了。是以如果你的hashCode方法依賴于對象中易變的資料,使用者就要當心了,因為此資料發生變化時,hashCode()方法就會生成一個不同的散列碼”。

繼續閱讀