先看完理解這篇:Java hashCode() 和 equals()的若干問題解答
實現高質量的equals方法的訣竅包括
- 使用==操作符檢查“參數是否為這個對象的引用”;
- 使用instanceof操作符檢查“參數是否為正確的類型”;
- 對於類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;
- 編寫完equals方法后,問自己它是否滿足對稱性、傳遞性、一致性;
- 重寫equals時總是要重寫hashCode;
- 不要將equals方法參數中的Object對象替換為其他的類型,在重寫時不要忘掉@Override注解。
public boolean equals(Object otherObject){ //測試兩個對象是否是同一個對象,是的話返回true if(this == otherObject) { //測試檢測的對象是否為空,是就返回false return true; } if(otherObject == null) { //測試兩個對象所屬的類是否相同,否則返回false return false; } if(getClass() != otherObject.getClass()) { //對otherObject進行類型轉換以便和類A的對象進行比較 return false; } A other=(A)otherObject; return Object.equals(類A對象的屬性A,other的屬性A)&&類A對象的屬性B==other的屬性B……; }
例子:
public class TestEquals { public static void main(String[] args) { Person2 p1 = new Person2("aa", 13); Person2 p2 = new Person2("aa", 13); Person2 p3 = new Person2("bb", 13); System.out.println(p1.equals(p2)); // true System.out.println(p1.equals(p3)); // false } } class Person2 { private String name; private int age; public Person2(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean equals(Object another) { //先判斷是不是自己,提高運行效率 if (this == another) return true; //再判斷是不是Person類,提高代碼的健壯性 if (another instanceof Person2) { //向下轉型,父類無法調用子類的成員和方法 Person2 anotherPerson = (Person2) another; //最后判斷類的所有屬性是否相等,其中String類型和Object類型可以用相應的equals()來判斷 if ((this.getName().equals(anotherPerson.getName())) && (this.getAge() == anotherPerson.getAge())) return true; } else { return false; } return false; } }
實現
hashCode
方法的通用約定
-
在應用程序的執行期間,只要對象的
equals
方法的比較操作所用到的信息沒有被修改,那么對這個同一對象調用多次,hashCode
方法必須始終如一地返回同一個整數。在同一個應用程序的多次執行過程中,每次執行所返回的整數可以不一致。 -
如果兩個對象根據
equals(Object)
方法比較是相等的,那么調用這兩個對象中任意一個對象的hashCode
方法都必須產生同樣的整數結果。反之,如果兩個對象hashCode
方法返回整數結果一樣,則不代表兩個對象相等,因為equals
方法可以被重載。 -
如果兩個對象根據
equals(Object)
方法比較是不相等的,那么調用這兩個對象中任意一個對象的hashCode
方法,則不一定要產生不同的整數結果。但,如果能讓不同的對象產生不同的整數結果,則有可能提高散列表的性能。
hashCode
散列碼計算(來自:Effective Java)
-
把某個非零的常數值,比如
17
,保存在一個名為result
的int
類型的變量中。 -
對於對象中每個關鍵域
f
(指equals
方法中涉及的每個域),完成以下步驟:-
為該域計算
int
類型的散列碼c:-
如果該域是
boolean
類型,則計算(f?1:0
)。 -
如果該域是
byte
,char
,short
或者int類型,則計算(int)f
。 -
如果該域是
long
類型,則計算(int)(f^(f>>>32))
。 -
如果該域是
float
類型,則計算Float.floatToIntBits(f)
。 -
如果該域是
double
類型,則計算Double.doubleToLongBits(f)
,然后按照步驟2.1.3,為得到的long
類型值計算散列值。 -
如果該域是一個對象引用,並且該類的
equals
方法通過遞歸地調用equals
的方式來比較這個域,則同樣為這個域遞歸地調用hashCode
。如果需要更復雜的比較,則為這個域計算一個范式(canonical representation)
,然后針對這個范式調用hashCode
。如果這個域的值為null
,則返回0
(其他常數也行)。 -
如果該域是一個數組,則要把每一個元素當做單獨的域來處理。也就是說,遞歸地應用上述規則,對每個重要的元素計算一個散列碼,然后根據步驟2.2中的做法把這些散列值組合起來。如果數組域中的每個元素都很重要,可以利用發行版本1.5中增加的其中一個
Arrays.hashCode
方法。
-
-
按照下面的公式,把步驟2.1中計算得到的散列碼
c
合並到result
中:result = 31 * result + c
; //此處31
是個奇素數,並且有個很好的特性,即用移位和減法來代替乘法,可以得到更好的性能:`31*i == (i<<5) - i, 現代JVM能自動完成此優化。
-
-
返回
result
-
檢驗並測試該
hashCode
實現是否符合通用約定。
@Override public int hashCode() { int result = 17; result = 31 * result + mInt; result = 31 * result + (mBoolean ? 1 : 0); result = 31 * result + Float.floatToIntBits(mFloat); result = 31 * result + (int)(mLong ^ (mLong >>> 32)); long mDoubleTemp = Double.doubleToLongBits(mDouble); result =31 * result + (int)(mDoubleTemp ^ (mDoubleTemp >>> 32)); result = 31 * result + (mString == null ? 0 : mMsgContain.hashCode()); result = 31 * result + (mObj == null ? 0 : mObj.hashCode()); return result; }