1. 程式人生 > >object物件重寫equals方法時為什麼需要重寫hashCode方法

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一定相同。

舉個例子

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;
		}
	}
}
如上,重寫了Str類的equals方法,卻沒有重寫它的hashCode方法,這就造成了混亂。

物件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;
		}
	}