為什么重寫equals方法時,要求必須重寫hashCode方法?


1 equals方法

Object類中默認的實現方式是  :   return this == obj  。那就是說,只有this 和 obj引用同一個對象,才會返回true。

而我們往往需要用equals來判斷 2個對象是否等價,而非驗證他們的唯一性。這樣我們在實現自己的類時,就要重寫equals.

 

按照約定,equals要滿足以下規則。

  • 自反性:  x.equals(x) 一定是true
  • 對null:  x.equals(null) 一定是false
  • 對稱性:  x.equals(y)  和  y.equals(x)結果一致
  • 傳遞性:  a 和 b equals , b 和 c  equals,那么 a 和 c也一定equals。
  • 一致性:  在某個運行時期間,2個對象的狀態的改變不會影響equals的決策結果,那么,在這個運行時期間,無論調用多少次equals,都返回相同的結果。

 

舉例:重寫equals方法

 1 class Test
 2 {
 3     private int num;
 4     private String data;
 5 
 6     public boolean equals(Object obj)
 7     {
 8         if (this == obj)
 9             return true;
10 
11         if ((obj == null) || (obj.getClass() != this.getClass()))
12             return false;
13 
14         //能執行到這里,說明obj和this同類且非null。
15         Test test = (Test) obj;
16         return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
17     }
18 
19     public int hashCode()
20     {
21         //重寫equals,也必須重寫hashCode。具體后面介紹。
22     }
23 
24 }

 

2 hashCode方法

這個方法返回對象的散列碼,返回值是int類型的散列碼。
對象的散列碼是為了更好的支持基於哈希機制的Java集合類,例如 Hashtable, HashMap, HashSet 等。


關於hashCode方法,一致的約定是:

  • 在某個運行時期間,只要對象的(字段的)變化不會影響equals方法的決策結果,那么,在這個期間,無論調用多少次hashCode,都必須返回同一個散列碼。
  • 如果2個對象通過equals調用后返回是true,那么這個2個對象的has
  • hCode方法也必須返回同樣的int型散列碼如果2個對象通過equals返回false,他們的hashCode返回的值允許相同(然而,程序員必須意識到,hashCode返回獨一無二的散列碼,會讓存儲這個對象的hashtables更好地工作。)

重寫了euqls方法的對象必須同時重寫hashCode()方法。

 

3 為什么必須重寫hashCode方法?

在上面的例子中,Test類對象有2個字段,num和data,這2個字段代表了對象的狀態,他們也用在equals方法中作為評判的依據。那么, 在hashCode方法中,這2個字段也要參與hash值的運算,作為hash運算的中間參數。這點很關鍵,這是為了遵守:2個對象equals,那么 hashCode一定相同規則。

也是說,參與equals函數的字段,也必須都參與hashCode 的計算。

 

4 重寫hashCode時注意事項

重寫hashCode方法時除了上述一致性約定,還有以下幾點需要注意:

(1)返回的hash值是int型的,防止溢出。

(2)不同的對象返回的hash值應該盡量不同。(為了hashMap等集合的效率問題)

(3)《Java編程思想》中提到一種情況

設計hashCode()時最重要的因素就是:無論何時,對同一個對象調用hashCode()都應該產生同樣的值。如果在講一個對象用put()添加進HashMap時產生一個hashCdoe值,而用get()取出時卻產生了另一個hashCode值,那么就無法獲取該對象了。所以如果你的hashCode方法依賴於對象中易變的數據,用戶就要當心了,因為此數據發生變化時,hashCode()方法就會生成一個不同的散列碼”。

舉個例子

public class Test {
    
    private int num;
    private String data;
    
    public Test(int num,String data){
        this.num = num;
        this.data = data;
    }
    
    public void setNum(int num) {
        this.num = num;
    }

    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;

        if ((obj == null) || (obj.getClass() != this.getClass()))
            return false;

        Test test = (Test) obj;
        return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
    }

    public int hashCode()
    {
        int hash = 7;
        hash = 31*hash+num;
        hash = 31*hash+data.hashCode();
        return hash;
        
    }
    
    public static void main(String[] args) {
        
        Map<Test,Integer> map = new HashMap<>();
        Test t1 = new Test(21,"ouym");
        map.put(t1, 1);
        t1.setNum(20);
        System.out.println(map.get(t1));
        
    }

}

輸出值為null,我的天吶,hashMap取不到值了。

不重寫equals和hashCode方法的話是不依賴於對象屬性的變化的,也就是說這里使用默認的hashCode方法可以取到值。但是我們重寫equal方法的初衷是判定name和num屬性都相等的Test對象是相等的,而不是說同一個對象的引用才相等,而num=21和num=20明顯不想等,所以這里hashCode返回值不同並不違背設計的初衷。注意上面代碼的使用陷阱。

 

 

 

來自:https://www.cnblogs.com/lulipro/p/5628750.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM