HashMap——HashMap中的indexFor方法

HashMap 中如何確定元素的位置

​ 衆所周知,在 jdk 1.7 中,HashMap 底層是由數組 + 鏈表的方式實現的,那我們在使用 HashMap 的時候,是如何將我們的 key-value put 到 HashMap 中的呢

HashMap 存放原理

​ 在理解 HashMap 的存放原理前,我們先來回想一下數組,當我們想給數組中的一個元素進行賦值時,我們至少需要知道兩個條件,一是數組的引用名稱,二是想要被賦值的數組元素的索引,即array[i]中的arrayi

​ 我們再來看看,在 jdk 1.7 中 HashMap 的結構:
在這裏插入圖片描述
​ 從上圖我們可以看到,HashMap 的主體是一個數組,這個數組中存儲的元素是一個Entry<k, v>類型的變量,這個變量有四個屬性:Object keyObject valueint hashEntry next,當兩個元素經過計算後的數組下標相同時,就是所謂的發生了 Hash 碰撞,這時,就需要在數組發生 Hash 碰撞的位置構造一個鏈表,將發生碰撞的元素以鏈表的形式,存放在數組中

​ 瞭解了 HashMap 的 構成,我們知道 HashMap 的本質也是數組,既然我們想向數組裏 put 元素,那我們必然需要知道數組的引用名稱和要被 put 的位置的下標,可是 HashMap 的 put 方法只有 key 和 value 兩個參數,沒有 int 類型的 index,那 HashMap 是如何確定每個元素會被存放到數組的哪個位置呢?

​ 這裏我們看一下 HashMap 源碼中的 put 部分:

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)  
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length); 
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
        Object k;  
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
            V oldValue = e.value;  
            e.value = value;  
            e.recordAccess(this);  
            return oldValue;  
        }  
    }  
    addEntry(hash, key, value, i);  
    return null;  
}

​ 這裏我們暫且不討論其他部分,直接看第 8 行代碼,結合下面的table[i]等變量可以看到,這行代碼通過indexFor函數返回的值,正是經過計算的數組下標,也就是說,HashMap 會在 put 元素時,通過元素的 hash 值以及當前數組的長度,來確定一個下標來存放元素

​ 這時我們不妨想一想,hash 值一般都是一個十分巨大的整數,例如12345643327864322等等(都是我瞎打的),而數組的長度一定是一個十分有限的數,假設是8,我們正常想通過這兩個數來獲取一個0~7的正數下標要怎麼做?毫無以爲,用hash % table.length,這樣不管 hash 是一個多大的數,我們都可以得到一個在數組索引範圍內的整數,那 HashMap 的 indexFor 函數中是如何做的呢?

HashMap 的 indexFor 函數

​ 這裏我們直接看 indexFor 函數的代碼:

/**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        return h & (length-1);
    }

​ 代碼很短,只有一行,我們可以看到,HashMap 中是通過 hash 和數組長度減一得到的結果進行一次&運算,這裏我們要先清楚&運算的概念:將兩個二進制數進行按位&操作時,只有兩個數對應的位上都爲 1,結果爲 1,否則都爲 0

​ 我們不妨帶進來一個數算一遍,這樣結果比較直接,假設我們的 h 的二進制表示是1101 0110(我瞎編的),數組的長度是8,二進制就是0000 1000,這時我們先進行length - 1的操作,得到0000 0111,這時再與 hash 進行&操作時,可以得到0000 0110,即十進制的6,而 HashMap 的容量,即數組的長度永遠都是2的次方,也就是說,table.length的二進制表示永遠都是一個1,其餘都是0的狀態,例如24次方160001 00005次方320010 0000,那也就是說明,table.length - 1得到的值永遠都是前一半都是0,後一半都是1,這種結構再與 hash 進行&操作時,得到的結果就和hash % table.length一樣了!

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