1. 程式人生 > >【Java基礎之重寫equals、hashCode和compareTo方法】什麼時候需要重寫重寫equals方法?為什麼重寫equals方法,一定要重寫HashCode方法?

【Java基礎之重寫equals、hashCode和compareTo方法】什麼時候需要重寫重寫equals方法?為什麼重寫equals方法,一定要重寫HashCode方法?

1.何時需要重寫equals() 當一個類有自己特有的“邏輯相等”概念(不同於物件身份的概念)。 2.設計equals() [1]使用instanceof操作符檢查“實參是否為正確的型別”。 [2]對於類中的每一個“關鍵域”,檢查實參中的域與當前物件中對應的域值。 [2.1]對於非float和double型別的原語型別域,使用==比較; [2.2]對於物件引用域,遞迴呼叫equals方法; [2.3]對於float域,使用Float.floatToIntBits(afloat)轉換為int,再使用==比較; [2.4]對於double域,使用Double.doubleToLongBits(adouble
) 轉換為int,再使用==比較; [2.5]對於陣列域,呼叫Arrays.equals方法。 3.當改寫equals()的時候,總是要改寫hashCode() 根據一個類的equals方法(改寫後),兩個截然不同的例項有可能在邏輯上是相等的,但是,根據Object.hashCode方法,它們僅僅是兩個物件。因此,違反了“相等的物件必須具有相等的雜湊碼”。 4.設計hashCode() [1]把某個非零常數值,例如17,儲存在int變數result中; [2]對於物件中每一個關鍵域f(指equals方法中考慮的每一個域): [2.1]boolean型,計算(f ? 0 : 1); [2.2]byte,char,short型,計算(int); [2.3]long型,計算(int) (f ^ (f>>>32)); [2.4]float型,計算Float.floatToIntBits
(
afloat); [2.5]double型,計算Double.doubleToLongBits(adouble)得到一個long,再執行[2.3]; [2.6]物件引用,遞迴呼叫它的hashCode方法; [2.7]陣列域,對其中每個元素呼叫它的hashCode方法。 [3]將上面計算得到的雜湊碼儲存到int變數c,然後執行 result=37*result+c; [4]返回result。
1.為什麼重寫equals方法,一定要重寫HashCode方法?
如果你過載了equals,比如說是基於物件的內容實現的,而保留hashCode的實現不變,那麼很可能某兩個物件明明是“相等”,而hashCode卻不一樣。這樣,當你用其中的一個作為鍵儲存到hashMap、hasoTable或hashSet中,再以“相等的”找另一個作為鍵值去查詢他們的時候,則根本找不到。
使用HashMap,如果key是自定義的類,就必須重寫hashcode()和equals()。
而對於每一個物件,通過其hashCode()方法可為其生成一個整形值(雜湊碼),該整型值被處理後,將會作為陣列下標,存放該物件所對應的Entry(存放該物件及其對應值)。 equals()方法則是在HashMap中插入值或查詢時會使用到。當HashMap中插入值或查詢值對應的雜湊碼與陣列中的雜湊碼相等時,則會通過equals方法比較key值是否相等,所以想以自建物件作為HashMap的key,必須重寫該物件繼承object的hashCode和equals方法。 
2.本來不就有hashcode()和equals()了麼?幹嘛要重寫,直接用原來的不行麼?
 HashMap中,如果要比較key是否相等,要同時使用這兩個函式!因為自定義的類的hashcode()方法繼承於Object類,其hashcode碼為預設的記憶體地址,這樣即便有相同含義的兩個物件,比較也是不相等的,例如,生成了兩個“羊”物件,正常理解這兩個物件應該是相等的,但如果你不重寫 hashcode()方法的話,比較是不相等的!
HashMap中的比較key是這樣的,先求出key的hashcode(),比較其值是否相等,若相等再比較equals(),若相等則認為他們是相等的。若equals()不相等則認為他們不相等。如果只重寫hashcode()不重寫equals()方法,當比較equals()時只是看他們是否為同一物件(即進行記憶體地址的比較),所以必定要兩個方法一起重寫。HashMap用來判斷key是否相等的方法,其實是呼叫了HashSet判斷加入元素是否相等。
引用別人說的一段話哈~
一般來說,如果你要把一個類的物件放入容器中,那麼通常要為其重寫equals()方法,讓他們比較地址值而不是內容值。特別地,如果要把你的類的物件放入雜湊中,那麼還要重寫hashCode()方法;要放到有序容器中,還要重寫compareTo()方法。
equals()相等的兩個物件,hashcode()一定相等;
equals()不相等的兩個物件,卻並不能證明他們的hashcode()不相等。換句話說,equals()方法不相等的兩個物件,hashcode()有可能相等。(我的理解是由於雜湊碼在生成的時候產生衝突造成的)。
反過來:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等

5.示例 下面的這個類遵循上面的設計原則,重寫了類的equals()和hashCode()。
package com.zj.unit; import java.util.Arrays; publicclass Unit { privateshortashort; privatecharachar; privatebyteabyte; privatebooleanabool; privatelongalong; privatefloatafloat; privatedoubleadouble; private Unit aObject; privateint[] ints; private Unit[] units; publicboolean equals(Object o) { if (!(o instanceof Unit)) returnfalse; Unit unit = (Unit) o; return unit.ashort == ashort && unit.achar == achar && unit.abyte == abyte && unit.abool == abool && unit.along == along && Float.floatToIntBits(unit.afloat) == Float .floatToIntBits(afloat) && Double.doubleToLongBits(unit.adouble) == Double .doubleToLongBits(adouble) && unit.aObject.equals(aObject) && equalsInts(unit.ints) && equalsUnits(unit.units); } privateboolean equalsInts(int[] aints) { return Arrays.equals(ints, aints); } privateboolean equalsUnits(Unit[] aUnits) { return Arrays.equals(units, aUnits); } publicint hashCode() { int result = 17; result = 37 * result + (intashort; result = 37 * result + (intachar; result = 37 * result + (intabyte; result = 37 * result + (abool ? 0 : 1); result = 37 * result + (int) (along ^ (along >>> 32)); result = 37 * result + Float.floatToIntBits(afloat); long tolong = Double.doubleToLongBits(adouble); result = 37 * result + (int) (tolong ^ (tolong >>> 32)); result = 37 * result + aObject.hashCode(); result = 37 * result + intsHashCode(ints); result = 37 * result + unitsHashCode(units); return result; } privateint intsHashCode(int[] aints) { int result = 17; for (int i = 0; i < aints.length; i++) result = 37 * result + aints[i]; return result; } privateint unitsHashCode(Unit[] aUnits) { int result = 17; for (int i = 0; i < aUnits.length; i++) result = 37 * result + aUnits[i].hashCode(); return result; } }