本文為博主原創,未經允許不得轉載:
1. equals 和 hashCode 方法之間的關係
這兩個方法都是 Object 的方法,意味著 若一個物件在沒有重寫 這兩個方法時,都會預設採用 Object 類中的方法實現,它們的關係為:
2.為什麼重寫equals 後需要重寫 hashCode
Effective Java 第三版 中 描述為什麼重寫equals 方法後必須重寫hashCode 方法:
每個覆蓋了equals方法的類中,必須覆蓋hashCode。如果不這麼做,就違背了hashCode的通用約定,也就是上面註釋中所說的。
進而導致該類無法結合所以與雜湊的集合一起正常運作,這裡指的是HashMap、HashSet、HashTable、ConcurrentHashMap。
上面註釋 為 Object 類中 hashCode 方法註釋:
If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
結論:如果重寫equals不重寫hashCode它與雜湊集合無法正常工作。
3. 以 HashMap 為例進行論證分析
檢視 hashMap 的 put 方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
檢視hash 方法的實現:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
檢視 get 方法的實現:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
java中HashMap的資料結構是陣列+連結串列+紅黑樹;這種資料結構,每個鍵值對都會被存在相應的地址中,從程式碼中可以看出HashMap是通過key的hashCode以及自身的容量來決定當前鍵值的儲存索引(桶)的,確定桶的位置後,再進入桶中同時判斷hashCode和equals兩個方法。那也就是說,如果hashCode不同,那麼HashMap就一定會建立一個新的Node鍵值物件。
HashMap在put一個鍵值對時,會先根據鍵的hashCode和equals方法來同時判斷該鍵在容器中是否已經存在,如果存在則覆蓋,反之新建。所以如果我們在重寫equals方法時,沒有重寫hashCode方法,那麼hashCode方法還是會預設使用Object提供的原始方法,而Object提供的hashCode方法返回值是不會重複的(也就是說每個物件返回的值都不一樣)。所以就會導致每個物件在HashMap中都會是一個新的鍵。
反向論證:若一個類中重寫了 equals 方法,沒有重寫hashCode方法;且該類的兩個物件具有不同屬性但 hashCode 相等,在hashMap 以該物件為鍵進行儲存時,會出現hash衝突現象,但發現該類重寫了equals 方法,且通過該類的equals 比較之後也是相等,就會出現 hashMap 中只儲存了一個物件,採用get 方法獲取時,就會獲取到別的物件,從而導致獲取物件錯亂。
因此 重寫equals 方法必須重寫 hashCode 方法,用來保證兩個物件通過equals()方法比較相等,那麼這兩個物件的hashCode一定相同 這一原則;