1. 程式人生 > >為什麼在重寫了equals()方法之後也必須重寫hashCode()方法

為什麼在重寫了equals()方法之後也必須重寫hashCode()方法

我們都知道Java語言是完全面向物件的,在java中,所有的物件都是繼承於Object類。Ojbect類中有兩個方法equals、hashCode,這兩個方法都是用來比較兩個物件是否相等的。

        對於值物件,==比較的是兩個物件的值,對於引用物件,比較的是兩個物件的地址。預設的equals方法同==,一般來說我們的物件都是引用物件,要重寫equals方法。再舉一個例子,現在有一個學生物件,有屬性學號跟姓名,現在我新建了一個學生物件,又從資料裡查出一個學生物件,這兩個物件的學號跟姓名都一樣,那這兩個物件是不是相等呢?一般情況下,除非你有特殊需求要處理,這兩個物件是相等的,可如果用==去比較,返回的結果是錯誤的。這時候我們就必須重寫equlas方法了。如果學號是主鍵,在equals方法裡,我們認為只要學號相同,就可以返回true。

       hashCode方法也是可以用來比較兩個物件是否相等的。但是我們很少使用,應該說是很少直接使用。hashCode方法返回的是一個int值,可以看做是一個物件的唯一編碼,如果兩個物件的hashCode值相同,我們應該認為這兩個物件是同一個物件。一般如果使用java中的Map物件進行儲存時,他會自動呼叫hashCode方法來比較兩個物件是否相等。所以如果我們對equals方法進行了重寫,建議一定要對hashCode方法重寫,以保證相同的物件返回相同的hash值,不同的物件返回不同的hash值。如上面的學生例子,如果學號相同,不管姓名相不相同,返回的hash值一定要是一樣的,這時我們的hash值只與學號有關。

例子:

我們先建立一個自己的類,作為hashmap的key

Java程式碼:  
  1. class key  
  2.  {  
  3.      int i ;  
  4.      public key(int i)  
  5.      {  
  6.      this.i = i;  
  7.      }  
  8.  }  

然後我們再定義一個類,作為hashmap的value

Java程式碼 : 
  1. class value  
  2.  {  
  3.      int j ;  
  4.      public value(int j)  
  5.      {  
  6.      this.j = j;  
  7.      }  
  8.      public String toString()  
  9.      {  
  10.      return ""+j;  
  11.      }  
  12.  }  

 測試:

Java程式碼: 
  1. public class Test  
  2.  {  
  3.      public static void main(String[] args)  
  4.      {  
  5.      HashMap hm = new HashMap();  
  6.      key k = new key(1);   //******(1)  
  7.      value v = new value(2);  
  8.      hm.put(k, v);  
  9.      if(hm.containsKey(k))  //*********(2)  
  10.       System.out.println(hm.get(k));  //*********(3)  
  11.      else  
  12.       System.out.println("dont have such a key");  
  13.     }  
  14.  }  

 注意:我這裡的hashmap中的key是自己new出的一個物件,然後把物件的引用作為key的,這裡突出了hashmap的查詢原理,hashmap是通過key的hashcode來找到hashmap中的key,這裡我在hashmap的key中是放一個物件的引用,我去拿key的時候也是通過這個引用,所以(1)處的key 與(2)、(3)處的key是完全一樣的,所以這段程式沒有任何問題,順利執行。

現在我把測試類改一下

Java程式碼:  
  1. public class Test  
  2.  {  
  3.      public static void main(String[] args)  
  4.      {  
  5.      HashMap hm = new HashMap();  
  6.      hm.put(new key(1),new value(2));  
  7.      if(hm.containsKey(new key(1)))  
  8.       System.out.println(hm.get(new key(1)));  
  9.      else  
  10.       System.out.println("dont have such a key");  
  11.      }  
  12.  }  

 注意區別,我這裡hashmap中key放的不是引用,而是new出來的物件,然後我去get或者containsKey的時候也通過new一個key去拿,雖然我們初始化內容完全相同,都是放 int 1 進去,也就是說物件內容完全相同,但最後結果確實輸出"dont have such a key"。
找原因,為什麼內容相同,但找不到這個key呢,前面說了hashmap是通過hashcode來找key的位置,這是關鍵,你每次new 一個新物件出來hashcode肯定不一樣,所以你拿不到你要的key。

解決方法,重寫你編寫的key類的hashcode方法。

Java程式碼:  
  1. class key  
  2.  {  
  3.      int i ;  
  4.      public key(int i)  
  5.      {  
  6.      this.i = i;  
  7.      }  
  8.      @Override  
  9.      public boolean equals(Object obj)  
  10.      {  
  11.      if(obj instanceof key)  
  12.      {  
  13.       if(((key)obj).i == i)  
  14.          return true;  
  15.      }  
  16.      return false;  
  17.      }  
  18.      @Override  
  19.      public int hashCode()  
  20.      {  
  21.      return i;  
  22.      }  
  23.  }  

 我們先不要看equals的重寫,這裡我們重寫了hashcode這個方法,讓它返回一個我們初始化進去的i,這樣你每次new一個物件,因為是通過hashcode找key,而你的hashcode有隻是值i,所以只要i相等,你就可以找到你的key的地址,注意,只是找到你要的key的地址,但key是不是同一個key還不一定。然後我們開始比較我們傳來的尋找value的key和hashmap中的key是不是同一個key,如果是那就找到了value。

在未重寫equals方法我們是繼承了object的equals方法,那裡的 equals是比較兩個物件的記憶體地址,顯然我們new了2個物件記憶體地址肯定不一樣,所以我們還要重寫equals這個方法,讓重寫後的equals方法來比較我們物件裡特有的東西。

重寫equals方法一般按照如下步驟:
1.先判斷這兩個比較的物件是不是同個型別,如果型別都不相同,肯定不相同;
2.如果型別相同,我們先要把Object向下轉型到我們的類型別,然後比較自己類特有的變數,這裡我只是比較了類裡i值是否相同,如果相同,則表明兩個物件是相同的(只是作為hashmap的key來說是相同的),這樣就可拿到hashmap的value了。

總結
hashmap中value的查詢是通過 key 的 hashcode 來查詢,所以對自己的物件必須重寫 hashcode 通過 hashcode 找到物件後會用 equals 比較你傳入的物件和 hashmap 中的 key 物件是否相同,所以要重寫 equals.

1、重寫equals方法時需要重寫hashCode方法,主要是針對Map、Set等集合型別的使用;

a: Map、Set等集合型別存放的物件必須是唯一的;

b: 集合類判斷兩個物件是否相等,是先判斷equals是否相等,如果equals返回TRUE,還要再判斷HashCode返回值是否ture,只有兩者都返回ture,才認為該兩個物件是相等的。

2、由於Object的hashCode返回的是物件的hash值,所以即使equals返回TRUE,集合也可能判定兩個物件不等,所以必須重寫hashCode方法,以保證當equals返回TRUE時,hashCode也返回Ture,這樣才能使得集合中存放的物件唯一。