關於HashMap自定義key重寫hashCode和equals的問題


 使用HashMap,如果key是自定義的類,就必須重寫hashcode()和equals()

hashcode()和equals()都繼承於object,在Object類中的定義為:

equals()方法在Object類中的定義:

public boolean equals(Object obj){
     return ( this == obj);
}
equals()的定義為:
public native int hashCode();

是一個本地方法,返回的對象的地址值。

      1.hashcode()和equals()是在哪里被用到的?什么用的?

     HashMap是 基於散列函數以數組和鏈表的方式實現的

     而對於每一個對象,通過其hashCode()方法可為其生成一個整形值(散列碼),而默認自定義類Student 的hashcode是根據對象的引用算的,該整型值被處理后,將會作為數組下標,存放該對象所對應的Entry(存放該對象及其對應值)。

     上邊由於兩個new Student(1,"aa")是不一樣的Student對象。而默認的Student類的hashcode是根據對象的引用算的。所以直接認為是兩個不一樣的對象,直接put進去了。所以需要重寫hashcode方法,如果hashcode不一樣則直接認為是不同對象

     equals()方法則是在HashMap中插入值或查詢時會使用到。當HashMap中插入 值或查詢值對應的散列碼與數組中的散列碼相等時,則會通過equals方法比較key值是否相等,所以想以自建對象作為HashMap的key,必須重寫 該對象繼承object的equals方法。

    
      2.本來不就有hashcode()和equals()了么?干嘛要重寫,直接用原來的不行么?
     

      HashMap中,如果要比較key是否相等,要同時使用這兩個函數!因為自定義的類的hashcode()方法繼承於Object類,其hashcode碼為默認的內存地 址,這樣即便有相同含義的兩個對象,比較也是不相等的,例如,

Student st1 = new Student("wei","man");

Student st2 = new Student("wei","man"); 

正常理解這兩個對象再存入到hashMap中應該是相等的,但如果你不重寫 hashcode()方法的話,比較是其地址,不相等!

      HashMap中的比較key是這樣的,先求出key的hashcode(),比較其值是否相等,若相等再比較equals(),若相等則認為他們是相等 的。若equals()不相等則認為他們不相等。如果只重寫hashcode()不重寫equals()方法,當比較equals()時只是看他們是否為 同一對象(即進行內存地址的比較),所以必定要兩個方法一起重寫。HashMap用來判斷key是否相等的方法,其實是調用了HashSet判斷加入元素 是否相等。
 
HashSet內部是通過HashMap實現。只有使用排序的時候才使用TreeMap。否知使用HashMap。
HashSet  set = new HashSet
set.add(new Student(1,"aa") );
set.add(new Student(1,"aa") );
set.add(new Student(2,"aa") );
 
結果set內的元素為3個,沒有去處重復的new Student(1,"aa") ?為什么呢?
這里由於兩個new Student(1,"aa")是不一樣的Student對象。而默認的Student類的hashcode是根據對象的引用算的。所以直接認為是兩個不一樣的對象,直接put進去了。所以需要重寫hashcode方法,如果hashcode不一樣則直接認為是不同對象,如下:

package com.svse.map;
public class Student {
   private int code;
   private String name;

   //重寫hashCode方法
   public int hashcode() {
    return code*name.hashCode();
  }

}

 
發現還是不對,還是put進去了呢?
這里重寫的hashcode是一樣的,所以還是put進去了。所以還需要重新equals方法。其實是有這樣一個規定,如果hahscode一樣時,則還需要繼續調用equals方式看看對象是否相等。如下即可實現:

輸出:

//重寫equals方法
public boolean equals(Object o) {
  if(o instanceof Student){
   Student stu=(Student) o;
   return name.equalsIgnoreCase(stu.getName().trim());
   }
  return false;
}


可以看到如果hashcode不一樣就直接認為是不一樣的對象,不需要再去equal比較,更加節省時間。
如果new Student(1,"aa")、new Student(1,"bb")。通過code和name算出的hashcode就可以算是不一樣的對象,就不需要再去equals比較。
往往HashSet中存放的對象是否相等的邏輯都需要自己定義,而並不會直接用默認的引用來算,即一般都會重新hashcode和equals方法,而且同時需要重寫。以后要注意哦。

HashMap的 putget也類似。
HashMap是底層實現時數組加鏈表。
        A.當put元素時:
              1.首先根據put元素的key獲取hashcode,然后根據hashcode算出數組的下標位置,如果下標位置沒有元素,直接放入元素即可。
              2.如果該下標位置有元素(即根據put元素的key算出的hashcode一樣即重復了),則需要已有元素和put元素的key對象比較equals方法,如果equals不一樣,則說明可以放入進map中。這里由於hashcode一樣,所以得出的數組下標位置相同。所以會在該數組位置創建一個鏈表,后put進入的元素到放鏈表頭,原來的元素向后移動。       
        B.當get元素時:
             根據元素的key獲取hashcode,然后根據hashcode獲取數組下標位置,如果只有一個元素則直接取出。如果該位置一個鏈表,則需要調用equals方法遍      歷鏈表中的所有元素與當前的元素比較,得到真正想要的對象。
  可以看出如果根據hashcdoe算出的數組位置盡量的均勻分布,則可以避免遍歷鏈表的情況,以提高性能。
   所以要求重寫hashcode時,也要重寫equals方法。以保證他們是相同的比較邏輯
 


免責聲明!

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



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