HashMap源碼解析(體系化總結)持續更新

Java集合繼承關係

 

 

典型集合源碼閱讀

HashTable

  • 繼承Dictionary類,實現Map類

  • 使用Syncronize實現線程安全,讀寫都做

  • 數組+單向鏈表存儲

  • key的尋址算法:(hash & 0x7FFFFFFF) % tab.length; 32位

  • containsValue:兩層遍歷,外層遍歷數組,內層單向遍歷鏈表,equals比對值

  • containsKey:先用key.hashcode計算所在數組位置,然後遍歷鏈表,比對entry.hash值以及key.equals

  • 最大存儲量:MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)+1

  • rehash:當前容量>=threshhold時,對hashtable擴容,然後就需要對之前的元素重新hash存入新table,

    擴容容量int newCapacity = (oldCapacity << 1) + 1;

  • remove:鏈表移除指定節點,1、首節點;2、中間節點

    • 擴展到算法場景:去除鏈表的指定節點

  • keySet(),entrySet(),使用Collections.synchronizedSet

  • values():Collections.synchronizedCollection

HashMap

  • 實現Map接口,繼承AbstractMap

  • 最大容量2^29次方

  • getNode方法(元素查找)

  • /**
      * Implements Map.get and related methods
      * 接口方法Map.get的具體實現
      * @param hash hash for key
      * @param key the key
      * @return the node, or null if none
    */
    final Node<K,V> getNode(int hash, Object key) {
        // https://blog.csdn.net/weixin_42340670/article/details/80779699
        // 聲明節點數組對象(就是該HashMap),首節點,遍歷的當前節點,數組長度,節點的鍵對象
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        // 節點數組賦值,數組長度賦值,通過位運算得到首節點
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            // 首先比對首節點,如果首節點的hash值和key的hash值相同 並且 首節點的鍵對象和key相同(地址相同或equals相等),則返回該節點
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            // 如果首節點不是,那麼看是否存在下一個節點,如果存在就繼續比對
            if ((e = first.next) != null) {
                // 如果首節點是樹節點,那麼就那找樹的方式查找
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                // 如果不是樹節點,就按照鏈表遍歷,逐個比對
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        // 如果沒有遍歷到指定節點,就返回null
        return null;
    }
  • resize是HashMap的擴容函數,當容量利用在 容量*負載因子(默認16*0.75)進行擴容,之前的兩倍。

  • HashMap是採用桶+鏈表,1.8開始,使用桶+鏈表+紅黑樹的形式。

  • 當鏈表的長度大於8時,就會將之前的鏈表轉成紅黑樹存儲,查找的時間複雜度由O(N)變換成O(logN)

  • 鏈表插入採用頭插法,高效插入。這也是相同key,後插入的會覆蓋前插入的原因。

  • 查找分兩步:查找需要分成兩步進行:

    • 計算鍵值對所在的桶;

    • 在鏈表上順序查找,時間複雜度顯然和鏈表的長度成正比

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