object物件重寫equals方法時為什麼需要重寫hashCode方法
在Java語言中,equals方法在使用時:
針對包裝物件,比較的是物件的值(包括 boolean,byte,char,short,int,long,float,double)
針對String物件,比較的也是String的值(因為String內部重寫了equals方法和hashCode方法)
針對其他object物件,比較的是兩個物件的引用是否指向同一個記憶體地址
而當我們重寫object物件的equals方法時,同時也要重寫hashCode方法,為什麼呢? 首先來看幾個定義
hash雜湊又稱之為雜湊,用於以常數平均時間執行插入、刪除和查詢的技術。(對排序不能得到有效的支援)
在物件儲存的散列表裡面,hashCode用來指定物件儲存的記憶體地址。而equals用來判斷物件的引用是否指向同一個地址,也就是判斷兩個物件的hashCode值是否一致
一般有約定
hashCode相同時,equals方法一定返回true。
equals方法返回true時,hashCode一定相同。
舉個例子
如上,重寫了Str類的equals方法,卻沒有重寫它的hashCode方法,這就造成了混亂。public class Main { public static void main(String[] args) { Str A = new Str(1); Str B = new Str(1); System.out.println("對比equals:" + A.equals(B)); //true System.out.println("對比hashCode值" + (A.hashCode() == B.hashCode())); //false Set<Str> temp = new HashSet<Str>(); System.out.println("新增A到HashSet中:" + temp.add(A)); //true System.out.println("新增B到HashSet中:" + temp.add(B)); //true } private static class Str{ public int val = 0; public Str(int val){ this.val = val; } public boolean equals(Str str){ return val == str.val; } } }
物件A和B的equals方法返回true
但是A和B的hashCode方法返回false
將兩個物件新增到HashSet中的時候,由於HashSet預設對比的是hashCode值是否一致,A和B的hashCode值不一致,所以同時新增成功了,這樣看,HashSet中就新增進去了兩個equals返回true的物件,這和set的設計理念有誤差,這就使得系統混亂了。
這也就是說,明明這兩個物件是相等的,但是hashCode值不一樣,所以當把A新增到HashSet中,而將B作為key去HashSet中查詢時,根本查不到A。而A和B明明又是相等的,從而出現錯誤。給人的直觀印象就是:“我要找的明明就是那個相等的物件,它的確也在集合裡,但就是查不到”
關於String的hashCode方法
要注意的是,String重寫了hashCode方法。這是因為散列表(hash表)操作中費時多的部分就是計算hashCode方法,所以在String類中的hashCode方法包含一個重要的優化:每個String物件內部都儲存了它的hashCode值,該值初始為0,但如果hashCode方法被呼叫,那麼這個值就將會被記住,下一次使用的時候可以直接調用出來,而不用再計算一次。之所以能這樣實現,是由於String類是不可改變的。所以hashCode值被計算之後也並不會發生變化。
String類的hashCode的實現,簡要摘錄如下:
public final class String{
private int hash = 0;
public int hashCode(){
if(hash != 0){
return hash;
}
for(int i=0; i<length(); i++){
hash = hash * 31 + (int) charAt(i);
}
return hash;
}
}