HashMap的數據是存儲在鏈表數組里面的。在對HashMap進行插入/刪除等操作時,都需要根據K-V對的鍵值定位到他應該保存在數組的哪個下標中。
而這個通過鍵值求取下標的操作就叫做哈希。
HashMap的數組是有長度的,Java中規定這個長度只能是2的倍數,初始值為16。
求哈希簡單的做法是先求取出鍵值的hashcode,然后在將hashcode得到的int值對數組長度進行取模。為了考慮性能,Java總采用按位與操作實現取模操作。
先看代碼:
static int indexFor(int h, int length) {
return h & (length-1);
}
按理說定位HashMap的角標應該是根據h%length來計算,為啥這里用的是h & (length-1)。原來作者是考慮到
位運算(&)效率要比代替取模運算(%)高很多,主要原因是位運算直接對內存數據進行操作,不需要轉成十進制,因此處理速度非常快。
那么,為什么可以使用位運算(&)來實現取模運算(%)呢?這實現的原理如下:
X % 2^n = X & (2^n - 1)
2n表示2的n次方,也就是說,一個數對2n取模 == 一個數和(2^n - 1)做按位與運算 。
假設n為3,則2^3 = 8,表示成2進制就是1000。2^3 -1 = 7 ,即0111。
此時X & (2^3 - 1) 就相當于取X的2進制的最后三位數。
從2進制角度來看,X / 8相當于 X >> 3,即把X右移3位,此時得到了X / 8的商,而被移掉的部分(后三位),則是X % 8,也就是余數。
上面的解釋不知道你有沒有看懂,沒看懂的話其實也沒關系,你只需要記住這個技巧就可以了。或者你可以找幾個例子試一下。
6 % 8 = 6 ,6 & 7 = 6
10 % 8 = 2 ,10 & 7 = 2
image
所以,return h & (length-1);
只要保證length的長度是2^n
的話,就可以實現取模運算了。而HashMap中的length也確實是2的倍數,初始值是16,之后每次擴充為原來的2倍。