java 中hashcode和equals 總結

1、概述

           在Java中hashCode的實現老是伴隨着equals,他們是緊密配合的,你要是本身設計了其中一個,就要設計另一個。固然在多數狀況下,這兩個方法是不用咱們考慮的,直接使用默認方法就能夠幫助咱們解決不少問題。可是在有些狀況,咱們必需要本身動手來實現它,才能確保程序更好的運做。html

1.1 規則

粗略總結一下在JavaDoc中所規定hashcode方法的合約:算法

     Objects that are equal must have the same hash code within a running process數組

   (在程序執行期間,若是兩個對象相等,那麼它們的哈希值必須相等)app

  注意,下面兩條常見錯誤想法:eclipse

  • Unequal objects must have different hash codes – WRONG!
  • Objects with the same hash code must be equal – WRONG!

關於hashcode,你必須知道的三件事ide

    1. Whenever you implement equals, you MUST also implement hashCode
    2. Never misuse hashCode as a key
    3. Do not use hashCode in distributed applications

詳情見:The 3 things you should know about hashCode()性能

 

對於hashCode,咱們應該遵循以下規則this

      1. 在一個應用程序執行期間,若是一個對象的equals方法作比較所用到的信息沒有被修改的話,則對該對象調用hashCode方法屢次,它必須始終如一地返回同一個整數。spa

      2. 若是兩個對象根據equals(Object o)方法是相等的,則調用這兩個對象中任一對象的hashCode方法必須產生相同的整數結果。.net

      3. 若是兩個對象根據equals(Object o)方法是不相等的,則調用這兩個對象中任一個對象的hashCode方法,不要求產生不一樣的整數結果。但若是能不一樣,則可能提升散列表的性能。

 

對於equals,咱們必須遵循以下規則

      對稱性:若是x.equals(y)返回是「true」,那麼y.equals(x)也應該返回是「true」。

      自反性:x.equals(x)必須返回是「true」。

      傳遞性:若是x.equals(y)返回是「true」,並且y.equals(z)返回是「true」,那麼z.equals(x)也應該返回是「true」。

      一致性:若是x.equals(y)返回是「true」,只要x和y內容一直不變,無論你重複x.equals(y)多少次,返回都是「true」。

任何狀況下,x.equals(null),永遠返回是「false」;x.equals(和x不一樣類型的對象)永遠返回是「false」。

1.2 做用

hashcode:

      常被系統用來快速檢索對象。

equals:

      一、若是沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址;

      二、String、Date等類對equals方法進行了重寫,它們比較的是所指向的對象的內容。

固然在重寫equals方法中,能夠指定比較對象的地址,若是這樣的話,就失去了重寫的意義,因此重寫,通常是比較對象的內容。

注意:equals方法不能做用於基本數據類型的變量。

1.3 關聯

至於hashcode與equals之間的關聯關係,咱們只須要記住以下便可:

  •       若是x.equals(y)返回「true」,那麼x和y的hashCode()必須相等。
  •       若是x.equals(y)返回「false」,那麼x和y的hashCode()有可能相等,也有可能不等。

 

所以,在重寫equals方法時,老是重寫hashCode方法。改寫後equals方法,使得兩個不一樣的實例在邏輯上是相等的;若是不重寫hashCode方法,則hashCode判斷兩個不一樣實例是不一樣的;致使違反「若是x.equals(y)返回「true」,那麼x和y的hashCode()必須相等。」

 

2、equals詳解

2.1 equals的設計指導

public class Person
{
    private String    name;
    private int age;
 
    public String getName()
    {
        return name;
    }
 
    public void setName(String name)
    {
        this.name = name;
    }
 
    public int getAge()
    {
        return age;
    }
 
    public void setAge(int age)
    {
        this.age = age;
    }
    
     @Override
     public boolean equals(Object other)
     {
         // 一、 自反性
         if (other == this)
         {
             return true;
         }
         // 二、判斷空值
         if (other == null)
         {
             return false;
         }
         // 三、對象所屬類的類型判斷
         if (!getClass().equals(other.getClass()))
         {
             return false;
         }
         // 四、對象的類型轉換
         Person person = (Person) other;
         // 五、針對所需比較的域進行比較
         if ((name.equals(person.name))&&(age==person.age))
         {
             return true;
         }
    
         return false;
     }
}
在第3點,對象所屬類的類型判斷,常常有兩種方式:
  1. getClass
  2. instanceof

如何選擇這兩種方式呢?

若是子類可以擁有本身的相等概念,則對稱性需求將強制採用getClass進行檢測。

若是由超類決定相等的概念,那麼就可使用instanceof進行檢測,這樣能夠在不一樣子類的對象之間進行相等的比較。

查看經典String中equals的方法,以下:
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;
}

雖然,String沒有對null值判斷,但在其註釋有解釋:

* Compares this string to the specified object.  The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object
that represents the same sequence of characters as this
* object.

 

 

 

3、hashCode詳解

3.1 hashCode設計指導

Josh Bloch在他的書籍《Effective Java》告訴咱們重寫hashcode方法的最佳實踐方式。
一個好的hashcode方法一般最好是不相等的對象產生不相等的hash值,理想狀況下,hashcode方法應該把集合中不相等的實例均勻分佈到全部可能的hash值上面。
下面就詳細介紹一下如何設計這個算法。這個算法是有現成的參考的,算法的具體步驟就是:
一、把某個非零常數值(通常取素數),例如17,保存在int變量result中;
二、對於對象中每個關鍵域f(指equals方法中考慮的每個域),計算int類型的哈希值c:
  • boolean型,計算(f ? 0 : 1);
  • byte,char,short型,計算(int)f;
  • long型,計算(int) (f ^ (f>>>32));
  • float型,計算Float.floatToIntBits(afloat);
  • double型,計算Double.doubleToLongBits(adouble)獲得一個long,而後再執行long型的計算方式;
  • 對象引用,遞歸調用它的hashCode方法;
  • 數組域,對其中每一個元素調用它的hashCode方法。

三、步驟2中,計算獲得的散列碼保存到int類型的變量c中,而後再執行result=31*result+c;(其中31能夠自選,最好是素數)

四、最後返回result。

核心公式:
result = 基數(31) * result + 哈希值(c:算法步驟2得到)
例如,Person類的hashCode
@Override
public int hashCode()
{
    int result = 17;
    result = 31 * result + age;
    result = 31 * result + stringToHashCode(name);
    return result;
}

private int stringToHashCode(String str)
{
    int result = 17;
    if (str.length()>0)
    {
        char[] value = str.toCharArray();
        for (int i = 0; i < value.length; i++)
        {
            char c = value[i];
            result = 31 * result + c;   
        }
    }
    return result;
}
查看String中hashCode代碼設計以下:
/** 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 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;
}


 

參考:

一、淺談Java中的hashcode方法

二、Java中hashCode的做用

三、Java提升篇(二六)——hashCode

四、hashCode與equals的區別與聯繫

五、Java核心技術卷1

相關文章
相關標籤/搜索