java基礎——對象的equals和hashcode覆蓋原則

由於若是隻覆蓋了equals而沒有覆蓋hashCode, 則兩個不一樣的instance a和b雖然equals結果(業務邏輯上)相等,但卻會有不一樣的hashcode,這樣hashmap裏面會同時存在a和b,而實際上咱們須要hashmap裏面只能保存其中一個,由於從業務邏輯方向看它們是相等的.

爲了能讓集合框架中的類如HashMap正常工做,必須保證同時覆蓋equals()和hashCode(),並且注意不要因爲寫錯了參數類型,而重載了這兩個方法,卻並無覆蓋它們,好比:
public boolean equals(Object obj) 寫成了public boolean equals(ClassXXX obj) 。

爲何在覆蓋equals時必定也要覆蓋hashCode呢? 下面用HashMap來闡述緣由,首先假設key1和key2的值在業務邏輯領域是相等的,即它們應該是同一個對象,HashMap已經存儲了key1,如今要查找key2是否存在,正確的結果應該是存在:
Java中的HashMap其實是一個鏈表數組,即首先HashMap是一個數組,而後數組中的每個元素是一個鏈表(更通用的概念能夠稱爲桶bucket,Java中的HashMap用Entry類描述鏈表的結點結構),HashMap在執行Put,Contains之類的操做時,會首先根據你提供的Key計算hashCode值,而後根據這個hashCode值在數組中找到某一個鏈表或桶(一般是找到鏈表的起始結點),這一步操做利用了hashCode()方法,若是你覆蓋了就會用你提供的方法,在找到某一個鏈表的起始結點後,就會遍歷鏈表,而後經過equals方法來尋找是否存在與Key的值相等的結點,若是執行equals方法後的結果相等,HashMap就認爲已經存在這個元素,這一步若是你覆蓋了equals方法就會用到你提供的equals方法。
經過上面的描述,咱們發現equals方法和hashCode方法若是不一樣時按你本身邏輯覆蓋的話,HashMap就會出問題。好比你只覆蓋了equals方法而沒有覆蓋hashCode方法,那麼HashMap在第一步尋找鏈表的時候會出錯,有一樣值的兩個對象Key1和Key2並不會指向同一個鏈表或桶,由於你沒有提供本身的hashCode方法,那麼就會使用Object的hashCode方法,該方法是根據內存地址來比較兩個對象是否一致,因爲Key1和Key2有不桶的內存地址,因此會指向不一樣的鏈表,這樣HashMap會認爲key2不存在,雖然咱們指望Key1和Key2是同一個對象;反之若是隻覆蓋了hashCode方法而沒有覆蓋equals方法,那麼雖然第一步操做會使Key1和Key2找到同一個鏈表,可是因爲equals沒有覆蓋,那麼在遍歷鏈表的元素時,key1.equals(key2)也會失敗(事實上Object的equals方法也是比較內存地址),從而HashMap認爲不存在Key2對象,這一樣也是不正確的。

如下內容摘自<<Effective Java>>
覆蓋equals時總要覆蓋hashCode,一個很常見的錯誤根源在沒有覆蓋hashCode方法。在每一個覆蓋了equals方法的類中,也必須覆蓋hashCode方法。若是不這樣作的話,就會違反Object.hashCode的通用約定,從而致使該類沒法結合全部基於散列的集合一塊兒正常工做,這樣的集合包括HashMap、HashSet和Hashtable。

下面是約定的內容,摘自Object規範[JavaSE6]:
1)在應用程序的執行期間,只要對象的equals方法所用到的信息沒有被修改,那麼對這同一個對象調用屢次,hashCode方法都必須始終如一地返回同一個整數。在同一個應用程序的屢次執行過程當中,每次執行所返回的整數能夠不一致。

2)若是兩個對象根據equals(Object)方法比較是相等的,那麼調用這兩個對象中任意一個對象的hashCode方法都必須產生一樣的整數結果。

3)若是兩個對象根據equals(Object)方法比較是不相等的,那麼調用這兩個對象中的任意一個對象的hashCode方法,則不必定要產生不一樣的整數結果。可是程序員應該知道,給不相同的對象產生大相徑庭的整數結果,有可能提供散列表(hash table)的性能。

若是覆蓋equals沒有覆蓋hashCode,將會違反上面的第2條:相等的對象必須具備相等的散列碼(hashCode)。Object類裏面的默認eqals方法是比較內存地址是否相等,默認的hashCode方法則是根據內存地址產生一個整數,因此Object類自己固然是符合上面規則的。當你覆蓋了equals後,內存地址不一樣的對象可能會相等,而若是這時你沒有覆蓋hashCode方法的話,hashCode仍是根據內存地址來生成,就會出現相等的對象具備不一樣的散列碼的狀況。程序員

相關文章
相關標籤/搜索