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

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

1. Object類中equals()方法源代碼以下所示:

/**
*	Object類中的equals()方法
*/
public boolean equals(Object obj) {
        return (this == obj);
    }

由以上源代碼知,Object類中的equals()方法是直接使用==運算符來判斷兩個對象相等的。java

  • 引用類型變量使用==時,比較的是引用類型變量指向的對象的內存地址
  • 基本類型使用==時,比較值

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()方法源代碼以下:app

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

2. String類中equals()方法和hashCode()方法

String類中部分源代碼以下所示:ide

/** 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()方法。ui

  1. 首先String類中定義了一個int類型的變量hash用來緩存String對象的hash值;
  2. 若是當前調用hashCode()方法的String對象在常量池沒有找到,而且該對象的length長度大於0,則繼續往下走,不然返回0;即String類默認""字符串的hashCode()值爲0;
  3. 遍歷字符數組,獲取每個字符的ASCII碼錶對應的值 和以前的hash值相加,這樣就保證了相同的字符串的hashCode()返回值相同,計算公式在註釋裏已經寫出來了:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
  4. 將計算出來的結果保存到hash變量中,並返回該值;

這裏爲何要乘以31呢?緣由是爲了性能,不單單指下降了計算速度,也下降了哈希衝突的機率。this

哈希衝突:此處指不一樣的字符串生成了相同的hashCode值。code

31是一個奇素數。若是乘數是偶數,而且乘法溢出的話,信息就會丟失,由於與2相乘等價於移位運算(低位補0)。使用素數的好處並不很明顯,可是習慣上使用素數來計算散列結果。 31 有個很好的性能,即用移位和減法來代替乘法,能夠獲得更好的性能: 31 * i == (i << 5)- i, 現代的 VM 能夠自動完成這種優化。這個公式能夠很簡單的推導出來。 ---- 《Effective Java

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

相關文章
相關標籤/搜索