Java HashMap的hash和indexFor函數

此文章,我們將一起了解一下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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章