HashMap中為什么數組的長度為2的冪次方

Java中HashCode算法詳解

Java中的集合,比如HashMap/HashSet/HashTable在實現上都用到了hashCode算法,用來計算元素在數組中的位置。hashCode是Object類中的一個方法,所以,所有的Java類都有這個方法,只是一些類對這個方法進行了覆寫,下面以String類的實現為例進行說明:

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;

}

其實這個算法的實現很簡單,以“hangzhou”這個字符串為例,計算過程如下:

第一步:int ‘h’

第二步:31 * (第一步結果) + int ‘a’

第三步:31 * (第二部結果) + int ‘n’

第四步:31 * (第三步結果) + int ‘g’

第五步:31 * (第四步結果) + int ‘z’

第六步:31 * (第五步結果) + int ‘h’

第七步:31 * (第六步結果) + int ‘o’

第八步: 31 * (第七步結果) + int ‘u’

可以得到“hangzhou”的hashcode為4740586。

為什么HashMap中的&位必須位奇數(length-1)

從key映射到HashMap數組的對應位置需要一個Hash函數:

index = Hash("hangzhou")

如何實現一個盡量分布均勻的hash函數呢?我們使用key的hashcode做某種運算:

index = hashCode("hangzhou") & (Length - 1) 其中,Length為HashMap的長度,下面來演示整個過程:

1、“hangzhou”的hashcode為4740586,二進制表示為100 1000 0101 0101 1110 1010

2、假定HashMap的長度為默認的16,則Length - 1為15,也就是二進制的1111

3、把以上兩個結果做與運算,得到的結果為1010,也就是index為10

可以說,Hash算法最終得到的index結果完全取決于hashCode的最后幾位。

假設,HashMap的長度為10,則Length - 1為9,也就是二進制的1001,通過Hash算法得到的最終index為8,當只有一個元素的時候這沒問題。但是我們再來試一個hashCode:100 1000 0101 0101 1110 1110時,通過Hash算法得到的最終的index也是8,另外還有100 1000 0101 0101 1110 1000得到的index也是8。也就是說,即使我們把倒數第二、三位的0、1變換,得到的index仍舊是8,說明有些index結果出現的幾率變大!!而有些index結果永遠不會出現,比如二進制0000.

這樣,顯然不符合Hash算法均勻分布的要求。

反觀,長度16或其他2的冪次方,Length - 1的值的二進制所有的位均為1,這種情況下,Index的結果等于hashCode的最后幾位。只要輸入的hashCode本身符合均勻分布,Hash算法的結果就是均勻的。

一句話,HashMap的長度為2的冪次方的原因是為了減少Hash碰撞,盡量使Hash算法的結果均勻分布。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容