如何編寫hashCode

閱讀經典——《Effective Java》05

上一篇文章介紹了如何覆蓋equals方法,通常在覆蓋了equals方法的類中也必須覆蓋hashCode方法。否則該類就無法與基于散列的集合類一起工作,比如HashMap和HashSet。至于這些集合類如何用到hashCode方法,網絡上有大量說明,本文重點討論如何編寫簡潔、高效的hashCode方法。

  1. hashCode通用約定
  1. 如何編寫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方法。
  • 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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容