計算hashCode的常見方法


  1. 把某個非零常數值,比如說17,保存在一個叫result的int類型的變量中。

2.對於對象中每一個關鍵域f(值equals方法中考慮的每一個域),完成以下步驟:

a.為該域計算int類型的散列嗎c:

i. 如果該域是boolean類型,則計算

[java] view plain copy
f?0:1
ii. 如果該域是byte、char、short或者int類型,則計算
[java] view plain copy
(int)f

iii.如果該域是long類型,則計算
[java] view plain copy
(int)(f ^ (f >>> 32))

iv. 如果該域是double類型,則計算
[java] view plain copy
Double.doubleToLongBits(f)

得到一個long類型的值,然后按照2.a.iii,對該long型值計算散列值。
vi. 如果該域是一個對象引用,並且該類的equals方法通過遞歸調用equals的方式來比較這個域,則同樣對這個域遞歸調用hashCode,如果要求一個更為復雜的比較,則為這個域計算一個“規范表示(canonical representation)”,然后針對這個范式調用hashCode。如果這個域為null,則返回0(或者是某個常數,但習慣上使用0)。

vii. 如果該域是一個數組,則把每一個元素當做單獨的域處理。也就是說,遞歸地應用上訴規則,對每一個重要的元素計算散列值,然后根據2.b的方法把這些散列值組合起來。

b.按照下面的公式,把步驟a得到的散列值組合到result中:

[java] view plain copy
result = 37 * result + c;

3.返回result
4.寫完了hashCode后,問自己相等的實例具有相同的hashCode么?假如不是,找出原因並修正。

在散列碼的計算過程中,把冗余域排除在計算之外是可以接受的。換句話說,如果一個域的值可以根據其他域值計算出來,則把這樣的域排除在外是可以接受的。

舉例,假如一個類PhoneNumber有三個關鍵域:areaCode,exchange,extension,都是short類型,則hashCode的計算過程為:

[java] view plain copy
@Override
public int hashCode(){
int result = 17;
result = result * 37 + areaCode;
result = result * 37 + exchange;
result = result * 37 + extension;
return result;
}
如果一個類是可變的,並且計算散列碼的代價也比較大,那么你應該考慮把散列碼緩存到對象的內部,而不是每次請求的時候都計算散列值。如果你覺得這種類型的大都數值會被用作散列鍵,那么你應該在實例被創建的時候計算散列值,否則,你可以選擇“延遲初始化”散列碼,一直到hashCode第一次調用才開始計算。
假如PhoneNumber這樣處理,那么代碼為:

[java] view plain copy
//延遲初始化
private volatile int hashCode = 0;

@Override
public int hashCode(){
if(hashCode == 0){
int result = 17;
result = result * 37 + areaCode;
result = result * 37 + exchange;
result = result * 37 + extension;
hashCode = result;
}
return hashCode;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM