重寫equals和hashCode

一、equals和hashcode方法都來自Object對象。

    API文檔中HashCode和equals方法定義java

public int hashCode()程序員

返回該對象的哈希碼值。支持此方法是爲了提升哈希表性能。hashCode的常規協定是編程

  • 在Java應用程序執行期間,在對同一對象屢次調用hashCode方法時,必須一致地返回相同的整數,前提是將對象進行equals比較時信息沒有被修改。
  • 若是根據equals()方法,兩個對象是相等的,那麼對這兩個對象中的每一個對象調用hashCode方法都必須生成相同的整數結果。
  • 若是根據equals()方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用hashCode方法不要求必定生成不一樣的整數結果,可是程序員應該意識到,爲不想等的對象生成不一樣的整數結果能夠提升哈希表的性能。
  • 實際上,由Object類定義的hashCode方法確實對針對不一樣的對象返回不一樣的整數。這通常是經過將該對象的內部地址轉換成一個整數來實現的,可是Java編程語言不須要這種實現技巧。

public boolean equals()數組

指示其餘某個對象是否與此對象「相等」。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。
  • Object 類的 equals 方法實現對象上差異可能性最大的相等關係;即,對於任何非空引用值 x 和 y,當且僅當 x 和 y 引用同一個對象時,此方法才返回 true(x == y 具備值 true)。
  • 注意:當此方法被重寫時,一般有必要重寫 hashCode 方法,以維護 hashCode 方法的常規協定,該協定聲明相等對象必須具備相等的哈希碼

二、equals重寫場景

    當一個類有本身特有的「邏輯相等」概念(不一樣於對象身份的概念)時,須要重寫equals方法性能

三、equals和hashCode方法設計

    3.一、equals方法設計

        【1】.使用instanceof操做符檢查「實參是否爲正確的類型」。優化

        【2】.對於類中的每個「關鍵域」,檢查實參中的域與當前對象中對應的閾值。this

            【2.1】對於非float和double類型的原語類型域,使用==比較spa

            【2.2】對於對象引用域,遞歸調用equals方法設計

            【2.3】對於float域,使用Float.floatToIntBits(afloat)轉化爲int,再使用==比較。

            【2.4】對於數組域,調用Arrays.equals方法.

    3.二、hashCode方法設計

        【1】.把某個非零常數值,保存在int變量result中
        【2】.對於對象中每個關鍵域f(指equals方法中考慮的每個域):
             【2.1】boolean型,計算(f?0:1)
             【2.2】byte,char,short型,計算(int)
             【2.3】long型,計算(int)(f ^(f >>>32))
             【2.4】float型,計算Float.floatToIntBits(afloat)
             【2.5】double型,計算Double.doubleToLongBits(abouble)獲得一個long,再執行[2.3]
             【2.6】對象引用,遞歸調用它的hashCode方法
             【2.7】數組域,對其中每一個元素調用它的hashCode方法
        【3】.將上面計算獲得的散列碼保存到int變量c中,而後執行result=37*result +c;
        【4】.返回result

四、使用示例

    在Object類中定義的hashCode方法代碼以下:

public native int hashCode();

這說明hashCode()本地機器相關的方法。jre6中String實現hashCode()的代碼以下:

/**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    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;
    }

    String實現hashCode()方法中利用了公式s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]。對這公式的解讀:

s[i]是string的第i個字符,n是String的長度。31爲啥呢?之因此選擇31,是由於它是個奇素數,若是乘數是偶數,而且乘法溢出的話,信息就會丟失,由於與2相乘等價於移位運算。使用素數的好處並非很明顯,可是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,能夠獲得更好的性能:31*i==(i<<5)-i。如今的VM能夠自動完成這種優化。

五、總結

  • 若是兩個對象調用equals(),返回結果爲true,那麼這兩個對象必定有相同的hashCode
  • 若是兩個對象調用equals(),返回結果爲false,這兩個對象能夠有相同的hashCode。可是這樣的結果會形成散列表退化成鏈表,性能下降。
  • 若是兩個對象的hashCode同樣,那麼這兩個對象必定相等。
  • 若是對象重寫了equals()方法,請程序員務必重寫hashCode(),遵循hashCode協定,提升系統能。
相關文章
相關標籤/搜索