此文章,我們將一起了解一下hash和indexFor方法在hashmap內部起什麼作用。hash和indexFor方法屬於HashMap類,爲什麼JDK開發者在key對象已經有他自己的hashcode方法的情況下還需要另一個hash函數?
首先看一下hash和indexFor方法的代碼:
/**
* Applies a supplemental hash function to a given hashCode, which
* defends against poor quality hash functions. This is critical
* because HashMap uses power-of-two length hash tables, that
* otherwise encounter collisions for hashCodes that do not differ
* in lower bits. Note: Null keys always map to hash 0, thus index 0.
*/
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
正如java doc對hash方法的描述:將hash函數作爲已給定的hashCode的一個補充,可以提高hash函數的質量。hash質量的好壞是非常重要的,因爲HashMap用2的次冪作爲表的hash長度,這就容易產生衝突,因爲hashCodes在低位不是不同的(hashCodes that do not differ in lower bits)。注意:Null 的key的hash值總是0,即他在table的索引爲0。
讓我們通過例子來幫助我們理解一下上面的話。加入說key object的hashCode()只返回三個值:31、63和95.31、63和95都是int型,所以是32位的。
31=00000000000000000000000000011111 63=00000000000000000000000000111111 95=00000000000000000000000001011111
現在加入HashMap的table長爲默認值16(2^4,HashMap的長度總是2的次冪)
假如我們不用hash函數,indexFor將返回如下值:
31=00000000000000000000000000011111 => 1111=15 63=00000000000000000000000000111111 => 1111=15 95=00000000000000000000000001011111 => 1111=15
爲什麼會這樣?因爲當我們調用indexFor函數的時候,它將執行31&15,,63&15和95&15的與操作,比如說95&15得出一下結果:
00000000000000000000000001011111 &00000000000000000000000000001111
也就是說(2^n-1)總會是一個1的序列,因此不管怎樣,最後會執行0&1的於然後得出0.
上面的例子,也就解釋了凡是在末尾全是1111的都返回相同的index,因此,儘管我們有不同的hashcode,Entry對象卻講都被存在table中index爲15的位置。
倘若我們使用了hash函數,對於上面每一個hashcode,經過hash作用作用後如下:
31=00000000000000000000000000011111 => 00000000000000000000000000011110 63=00000000000000000000000000111111 => 00000000000000000000000000111100 95=00000000000000000000000001011111 => 00000000000000000000000001011010
現在在通過新的hash之後再使用indexFor將會返回:
00000000000000000000000000011110 =>1110=14 00000000000000000000000000111100 =>1100=12 00000000000000000000000001011010 =>1010=10
在使用了hash函數之後,上面的hashcodes就返回了不同的index,因此,hash函數對hashmap裏的元素進行了再分配,也就減少了衝突同時提高了性能。
hash操作最主要的目的就是在最顯著位的hashcode的差異可見,以致於hashmap的元素能夠均勻的分佈在整個桶裏。
有兩點需要注意:
如果兩個key有相同的hashcode,那他們將被分配到table數組的相同index上
如果兩個key不具有相同的hashcode,那麼他們或許可能,或許也不可能被分配到table數組相同的index上。
英文參考文檔:http://javapostsforlearning.blogspot.in/2014/02/hash-and-indexfor-method-in-hashmap.html