JAVA基礎複習(二),HashMap的實現原理

HashMap的JDK1.8的實現主要是由數組+單向鏈表+紅黑樹組成的,數據存儲結構如下圖。(JDK1.8之前採用的只是,數組+單向鏈表)

當鏈表長度大於8時會先判斷下,數組的長度是否小於64,如果小於則進行數組擴容;否則將鏈表結構轉換爲紅黑樹。

 

HashMap默認數組長度爲16,在往map中放元素時,首先會根據key進行Hash操作拿到hash值,然後進行取模操作,hash%16

得到在table中的index。當Key的hash值相同的時候,判斷key的equals方法,一樣替換原有的value,不一致並且鏈表長度小於8則將該元素放到原有元素的next節點中,並將數組中的元素替換成最新的節點。總的來說,如果該數組在該位置上已經存放了其他元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放入鏈尾.如果數組中該位置沒有元素,就直接將該元素放到數組的該位置上。

 

 

一  初始化

構造方法如上,前3個都只是對屬性進行初始化設值,第4個方法會將參數中的Entry進行put操作,調用方法:putMapEntries(m, false)

主要邏輯:1.判斷是否初始化,沒有進行初始化操作,設置數據的大小;2.判斷參數m的size是否大於數據的最大長度,大於進行擴容

3.將m中的entry放到map中;

 

二  主要方法刨析

put方法


public V put(K key, V value) {
    //調用putVal()方法完成
    return putVal(hash(key), key, value, false, true);
}
 
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //判斷table是否初始化,否則初始化操作
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //計算存儲的索引位置,如果沒有元素,直接賦值
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        //節點若已經存在,執行賦值操作
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //判斷鏈表是否是紅黑樹
        else if (p instanceof TreeNode)
            //紅黑樹對象操作
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            //爲鏈表,
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //鏈表長度8,將鏈表轉化爲紅黑樹存儲
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                //key存在,直接覆蓋
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    //記錄修改次數
    ++modCount;
    //判斷是否需要擴容
    if (++size > threshold)
        resize();
    //空操作
    afterNodeInsertion(evict);
    return null;
}
 

get方法



獲取該hash值在數組中的位置通過(n-1)&hash 得到index值,取值時每次首先判斷是不是第一個node,並且在獲取第二個節點的時候判斷下是不是紅黑樹類型的,是紅黑樹類型的通過getTreeNode方法去獲取value值,不然循環遍歷鏈表equals(k)進行判斷獲取。

補充:在位運算中hash%n 和  (n-1)&hash的值是相等的,所以在HashMap中用 hash%n來獲取數組下標,然後通過(n-1)&hash重新獲得下標值,但是爲什麼不再次調用hash%n來再次獲取呢。。?

 

參考:
原文鏈接:https://blog.csdn.net/qq_41345773/article/details/92066554
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章