爲何重寫equals方法同時必定要重寫hashCode方法

問題:爲何重寫equals方法必定要同時重寫hashCode方法?

首先拋出答案:這和集合的某些操做有關(好比HashSet的add方法)java

HashSet add(E e) 方法以下:數組

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
}
複製代碼

在HashSet的內部維護了一個HashMap,能夠看到對於HashSet的add操做委託給了HashMap的put 操做。bash

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;
    //內部維護的HashMap
    private transient HashMap<E,Object> map;
複製代碼

繼續查看HashMap的put方法以下:app

public V put(K key, V value) {
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
        addEntry(hash, key, value, i);
        return null;
    }
複製代碼

咱們大體分析一下這段代碼邏輯:ui

  1. 首先根據添加元素的hash值尋找到能夠放置的Entry數組的位置。
  2. 而後在這個合適的位置上根據Entry鏈查找是否有equals相同的值,若是有就返回舊值,若是沒有就插入這個HashMap。(注意:若是這個Entry鏈不存在則直接建立entry並插入這個合適的位置,若是entry鏈只有一個entry節點,那麼直接插入)

咱們再來看看HashMap的hash方法以下:spa

final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
複製代碼

咱們能夠看到h ^= k.hashCode();這個調用,這也正好是調用了元素的hashCode方法。code

重點來了:若是咱們不重寫hashCode方法會產生什麼樣的結果?對象

咱們回頭看看HashMap的put方法,若是這個時候重複添加具備相同內容的對象,若是不重寫hashCode,那麼這倆個對象就有可能會在Entry數組的不一樣位置分別構建entry,然而它們的內容倒是一致的,即便你重寫了equals方法並不必定能保證HashSet中的元素惟一。接口

因此,對於集合操做好比Set類型接口,Map類型接口而言,重寫了equals方法必定要重寫hashCode 保證元素惟一性。ip


而對於equals使用的準則有:

自反性:對於任何非空引用值 x,x.equals(x) 都應返回 true。

對稱性:對於任何非空引用值 x 和 y,當且僅當 y.equals(x) 返回 true 時,x.equals(y) 才應返回 true。

傳遞性:對於任何非空引用值 x、y 和 z,若是 x.equals(y) 返回 true, 而且 y.equals(z) 返回 true,那麼 x.equals(z) 應返回 true。

一致性:對於任何非空引用值 x 和 y,屢次調用 x.equals(y) 始終返回 true 或始終返回 false, 前提是對象上 equals 比較中所用的信息沒有被修改。

非空性:對於任何非空引用值 x,x.equals(null) 都應返回 false。

相關文章
相關標籤/搜索