equals & hashCode

equals() 與hasCode() 都定義在Object.java中。equals 默認使用「==」來比較對象。java

public boolean equals(Object obj) {
        return (this == obj);
    }
   
   public native int hashCode();

在對於hashCode()的javadoc中明確代表: hashCode是用在hash表中的。 equals相同,hashCode必定相同(但hashCode相同,equals不必定相同),這樣有利於提升hash表的效率。算法

eg. 驗證: hashCode相同,equals不必定相同源碼分析

public static void main(String[] args) {
        Long a  = 1L;
        Integer b = 1;
        System.out.println(b.hashCode() == a.hashCode()); // true
        System.out.println(b.equals(a)); // false

    }

執行結果: true、 false。ui

java.lang下各種的重寫

對於String:     this

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

對於Boolean:
 hashCode() 爲定值: true->1231! fale -> 1237。 equals() 先斷定是不是本類的實例再比較值。spa

public int hashCode() {
        return value ? 1231 : 1237;
    }
    public boolean equals(Object obj) {
        if (obj instanceof Boolean) {
            return value == ((Boolean)obj).booleanValue();
        }
        return false;
    }

對於比Integer範圍小的short、byte、char :
hashCode() 直接強轉int。 equals() 先斷定是不是本類的實例再比較值。.net

public int hashCode() {
        return (int)value;
    }

   public boolean equals(Object obj) {
        if (obj instanceof Short) {
            return value == ((Short)obj).shortValue();
        }
        return false;
    }

對於比Integer範圍大的Long、float、double: 
             hashCode(): 則須要處理轉換成int。double比較特殊些:是先處理爲long,再用long取hashCode()的方式轉int;
             equals():  Long是直接比較數值; 而float、double是要轉成int進行比較(按javadoc中所述 須要考慮NaN的問題),NaN直接返回定值code

public static void main(String[] args) {
        float a = Float.NaN;
        Float b = Float.NaN;
        System.out.println(a == b); // flase
        System.out.println(b.equals(b)); // true
        float a2 = 1f;
        Float b2 = 1f;
        System.out.println(a2 == b2); // true
        System.out.println(b2.equals(b2)); // true
    }

執行結果: false、true、true、true。 對象

NaN與任何浮點數(包括自身)的比較結果都爲假,這是NaN獨有的特性,因此可使用與本身相比來肯定當前數值是否是一個正常的數.blog

結論: 若是僅僅考慮非NaN的問題能夠直接用floatValue()、doubleValue()方法。但由於float與double有NaN因此須要考慮!

源碼以下:

Long:
   public static int hashCode(long value) {
        return (int)(value ^ (value >>> 32));
    } 
  public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }

Float: 

   /**
     * A constant holding a Not-a-Number (NaN) value of type
     * {@code float}.  It is equivalent to the value returned by
     * {@code Float.intBitsToFloat(0x7fc00000)}.
     */
  public static final float NaN = 0.0f / 0.0f;

  public static int hashCode(float value) {
        return floatToIntBits(value);
    }

   public boolean equals(Object obj) {
        return (obj instanceof Float)
               && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
    }

    public static int floatToIntBits(float value) {
        int result = floatToRawIntBits(value);
        // Check for NaN based on values of bit fields, maximum
        // exponent and nonzero significand.
        if ( ((result & FloatConsts.EXP_BIT_MASK) ==
              FloatConsts.EXP_BIT_MASK) &&
             (result & FloatConsts.SIGNIF_BIT_MASK) != 0)
            result = 0x7fc00000;
        return result;
    }

Double:
    public int hashCode() {
        long bits = doubleToLongBits(value);
        return (int)(bits ^ (bits >>> 32));
    }

    public static long doubleToLongBits(double value) {
        long result = doubleToRawLongBits(value);
        // Check for NaN based on values of bit fields, maximum
        // exponent and nonzero significand.
        if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
              DoubleConsts.EXP_BIT_MASK) &&
             (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
            result = 0x7ff8000000000000L;
        return result;
    }

    /**
     * A constant holding a Not-a-Number (NaN) value of type
     * {@code double}. It is equivalent to the value returned by
     * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
     */
    public static final double NaN = 0.0d / 0.0;

equals和hashcode爲何要一塊兒重寫

維護 hashCode 方法的常規協定,該協定聲明相等對象必須具備相等的哈希碼。 

hashcode是用於散列數據的快速存取,如利用HashSet、HashMap、Hashtable類來存儲數據時,都是根據存儲對象的hashcode值來進行判斷是否相同的。

hashSet的核心就是經過HashMap的key來實現的

/**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

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

經過hashMap的源碼可見: hash(Object key)對於定位對象在table的位置相當重要,其中就會使用對象的hashCode值。

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

因此在老版本(1.7)hashMap的實現中對於hash定位相同的值將使用拉鍊法存儲:存儲table位置上的是一個Map.Entry對象鏈表。新值對若是hash定位相同,equals不一樣,則封裝成Map.Entry對象將插入進該鏈表的頭部,next指向鏈表原頭部;該鏈表查找算法的時間複雜度爲0(n)。而在1.8改成當鏈表長度超過TREEIFY_THRESHOLD(默認 8) <實則是9>則轉爲紅黑樹存儲,因紅黑樹的特性,查找算法的時間複雜度爲O(log2 n)。(hashTable的處理仍是拉鍊法)

Collection的源碼分析

相關文章
相關標籤/搜索