Java 中全部的類都繼承自 Object 類,Object 類中有個返回 hashCode 的本地方法。java
public native int hashCode();
複製代碼
在文檔的註釋中很清楚的說明了 hashCode 的做用,和它應該知足的一些要求。算法
做用:給一個對象返回一個 hashCode 值,這個值在 hash table 的數據結構中有重要的做用。例如,肯定放置在 hash table 哪一個索引位置,hash 衝突的頻率。segmentfault
要求:數組
一般的 hashCode 生成方法是將對象的內存地址轉換成一個整型數,這樣就能爲不一樣的對象返回一個不同的 hashCode。可是這種方法不能知足上面的第二個條件,因此這種實現也不是 Java 語言所必須的實現方法。數據結構
String 類也是繼承自 Object 類,它重寫了 hashCode() 方法。優化
/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
複製代碼
在 String 中計算的 hashCode 會存儲在 hash 變量中,而且只會計算一次。由於 String 是 final 的,而且一個 String 對象被初始化後沒法修改,因此它的 hashCode 不會變化。spa
for 循環計算 hashCode 的方法是根據如下式子:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]。設計
31 是一個質數(Prime number),質數也稱爲素數。質數是大於 1 的天然數,且只能被 1 和其自己整除。code
選擇 31 的緣由大概有如下幾點:對象
一個數乘質數後的結果,只能被 1 、質數、乘數還有結果自己整除。計算 hashCode 選擇一個優質質數能夠下降 hash 的衝突率。
31 (2 << 5 - 1),在計算的過程當中能夠被 JVM 優化。
相信第二點不少同窗都可以理解,如今解釋一下第一點。
咱們列舉一下 100 之內左右的質數:2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97。
從上面的質數中選擇三個小中大質數:2,31,97。分析公式 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 中的每一項,都是一個數乘質數的平方項。因此咱們計算一下每一個質數的 n 次方,咱們選擇 n = 5。那麼結果以下:
質數 | 結果 |
---|---|
2 | 2^5 = 32 |
31 | 31^5 = 28,629,151 |
97 | 97^5 = 8,587,340,257 |
能夠看到經過質數 2 計算後的 hashCode 值是一個比較小的值,而經過質數 97 計算後的 hashCode 是一個比較大的值,而 31 比較適中。
咱們能夠認爲 hashCode 的範圍若過小,可能會增長 hash 衝突的機率。而計算 hashCode 的計算乘子太大容易致使整型數的溢出(這裏並非說選擇 31 不會致使溢出,是指一個致使溢出的速率),從而也會致使 hash 衝突的機率。31 能夠有效的減輕這兩點。
更詳細的內容能夠看一下 stackoverflow 上面的這個問題:Why does Java's hashCode() in String use 31 as a multiplier?
根據《Effective Java》第二版中的第 9 條,對於咱們本身編寫的類,覆蓋 equals 方法時須要覆蓋 hashCode 方法。緣由在前面說過。
那麼如何設計一個 hashCode 算法,書中設計了一個算法:
科普:爲何 String hashCode 方法選擇數字31做爲乘子
Why does Java's hashCode() in String use 31 as a multiplier?
《Effective Java》