天天看點

equals()方法和hashCode()方法詳解

equals()

方法和

hashCode()

方法詳解

1.

Object

類中

equals()

方法源代碼如下所示:

/**
*	Object類中的equals()方法
*/
public boolean equals(Object obj) {
        return (this == obj);
    }
           
由以上源代碼知,

Object

類中的

equals()

方法是直接使用

==

運算符來判斷兩個對象相等的。
  • 引用類型變量使用

    ==

    時,比較的是引用類型變量指向的對象的記憶體位址
  • 基本類型使用

    ==

    時,比較值

Objcect

類中的

hashCode

源代碼如下:

/**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>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.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
	public native int hashCode();// java8中的hashCode方法,
           
上面的注釋中有說明如下幾點:
  • 對象的

    hashCode

    值通常是根據對象的記憶體位址計算得來
  • 兩個對象

    equals()

    結果為

    true

    時,兩個對象的

    hashCode

    值一定相等,不同對象的

    hashCode

    不等
  • native

    辨別此方法不是

    java

    語言實作

Object

類中的

toString()

方法源代碼如下:

public String toString() {
    	// 從這裡就能看出列印對象時不重寫toString()方法時,就會列印出對象的hashCode值
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
           

2.

String

類中

equals()

方法和

hashCode()

方法

String

類中部分源代碼如下所示:
/** The value is used for character storage. */
    private final char value[];
  /** Cache the hash code for the string */
    private int hash; // Default to 0
  /**
  * 無參構造方法
  */
  public String() {
        this.value = "".value;
    }
  /**
  * 有參構造方法
  */
  public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
  /**
  *String類重寫的equals方法
  */
  public boolean equals(Object anObject) {
        if (this == anObject) {// 此處的this指向a.equals(b)的a對象,即誰調用指向誰
            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;
    }
   /**
     * 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

類已經重寫了

equals()

方法和

hashCode()

方法。

String

類重寫的

equals()

方法判斷流程如下:

  1. 使用

    ==

    來判斷兩個對象的記憶體位址是否相同,相同傳回

    true;

  2. 如果兩個對象的記憶體位址不同,程式繼續往下走,判斷另一個對象是否是

    String

    類型的;
  3. 如果比較對象不是

    String

    類型,直接傳回

    false

  4. 如果是

    String

    類型的,進行類型強轉;
  5. 比較兩個

    String

    的字元數組長度,如果長度不同,傳回

    false

  6. 利用

    while

    循環來逐位比較字元是否相等,直到循環結束,所有字元都相等,則傳回

    true

    ,否則傳回

    false

    ;

下面來看一下重寫的

hashCode()

方法。

  1. 首先

    String

    類中定義了一個

    int

    類型的變量

    hash

    用來緩存

    String

    對象的

    hash

    值;
  2. 如果目前調用

    hashCode()

    方法的

    String

    對象在常量池沒有找到,并且該對象的

    length

    長度大于 ,則繼續往下走,否則傳回 ;即

    String

    類預設

    ""

    字元串的

    hashCode()

    值為 ;
  3. 周遊字元數組,擷取每一個字元的

    ASCII

    碼表對應的值 和之前的

    hash

    值相加,這樣就保證了相同的字元串的

    hashCode()

    傳回值相同,計算公式在注釋裡已經寫出來了:

    s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

  4. 将計算出來的結果儲存到

    hash

    變量中,并傳回該值;

這裡為什麼要乘以

31

呢?原因是為了性能,不僅僅指降低了計算速度,也降低了哈希沖突的機率。

哈希沖突:此處指不同的字元串生成了相同的

hashCode

值。

31

是一個奇素數。如果乘數是偶數,并且乘法溢出的話,資訊就會丢失,因為與2相乘等價于移位運算(低位補

)。使用素數的好處并不很明顯,但是習慣上使用素數來計算散列結果。

31

有個很好的性能,即用移位和減法來代替乘法,可以得到更好的性能:

31 * i == (i << 5)- i

, 現代的

VM

可以自動完成這種優化。這個公式可以很簡單的推導出來。 ---- 《

Effective Java

素數:質數又稱素數,指在一個大于1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數。