閱讀經典——《Effective Java》05
上一篇文章介紹了如何覆蓋equals方法,通常在覆蓋了equals方法的類中也必須覆蓋hashCode方法。否則該類就無法與基于散列的集合類一起工作,比如HashMap和HashSet。至于這些集合類如何用到hashCode方法,網絡上有大量說明,本文重點討論如何編寫簡潔、高效的hashCode方法。
- hashCode通用約定
- 如何編寫hashCode
hashCode通用約定
Java規范中要求hashCode方法必須滿足:
- 如果兩個對象equals結果為真,那么他們的hashCode結果必須一致。
- 如果兩個對象equals結果為假,那么他們的hashCode結果不一定不同。但是,不同的hashCode結果有利于提高散列表性能。
如何編寫hashCode
理想情況下,散列函數應該把集合中不相等的實例均勻地分布到所有可能的散列值上。雖然很難做到,但下面的方案不失為一種良好的解決方法。
- 1.把某個非零的常數值,比如17,保存在變量
int result
中。
- 2.對于對象中每個關鍵域
f
(關鍵域指equals方法比較時用到的域),完成以下步驟: - a. 為該域計算int類型的散列碼c:
- i. 如果該域是boolean類型,則計算
f ? 1:0
。 - ii. 如果該域是byte、char、short或者int類型,則計算
(int) f
。 - iii. 如果該域是long類型,則計算
(int) (f ^ (f >>> 32))
。 - iv. 如果該域是float類型,則計算
Float.floatToIntBits(f)
。 - v. 如果該域是double類型,則計算
Double.doubleToLongBits(f)
,然后按照步驟2.a.iii,為得到的long類型值計算散列值。 - vi. 如果該域是一個對象引用,并且該類的equals方法通過遞歸地調用equals的方式來比較這個域,則同樣為這個域遞歸地調用hashCode。
- vii. 如果該域是一個數組,則要把每一個元素當做單獨的域來處理。也就是說,遞歸地應用上述規則,對每一個重要的元素計算一個散列碼,然后根據步驟2.b中的做法把這些散列值組合起來。如果數組域中的每個元素都很重要,可以利用
Arrays.hashCode
方法。
- i. 如果該域是boolean類型,則計算
- b. 按照下面的公式,把步驟2.a中計算得到的散列碼c合并到result中。
result = 31 * result + c;
- 3.返回result
- 4.寫完了hashCode方法后,問問自己“相等的實例是否都具有相等的散列碼”。要編寫單元測試來驗證你的推斷。如果相等的實例有著不同的散列碼,則要找出原因,并修正錯誤。
為了更清晰的說明這幾個步驟具體如何實現,我們舉一個簡單的例子。考慮如下Person類的hashCode實現。
/**
* Person并不是一個非常合適的例子,此處僅用于說明如何按照步驟編寫hashCode方法。
* 實際中一般不會按照name、gender、age來判定是否為同一個人。
*/
public class Person {
private boolean gender;
private int age;
private String name;
public Person(boolean gender, int age, String name) {
this.gender = gender;
this.age = age;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof Person)) return false;
Person p = (Person) obj;
return p.gender == gender && p.age == age && p.name == name;
}
@Override
public int hashCode() {
int result = 17;
int c = gender ? 1 : 0;
result = 31 * result + c;
c = age;
result = 31 * result + c;
c = name.hashCode();
result = 31 * result + c;
return result;
}
}
Person類有三個關鍵域gender、age和name,而且分別屬于不同的類型boolean、int和String。按照前面介紹的步驟很容易實現hashCode,經過測試,相等的實例都具有相等的散列碼。
關注作者或文集《Effective Java》,第一時間獲取最新發布文章。
參考資料
java中HashMap詳解 alex09