1. 程式人生 > >深入分析hashmap

深入分析hashmap

一、傳統 HashMap的缺點

(1)JDK 1.8 以前 HashMap 的實現是 陣列+連結串列,即使雜湊函式取得再好,也很難達到元素百分百均勻分佈。

(2)當 HashMap 中有大量的元素都存放到同一個桶中時,這個桶下有一條長長的連結串列,這個時候 HashMap 就相當於一個單鏈表,假如單鏈表有 n 個元素,遍歷的時間複雜度就是 O(n),完全失去了它的優勢。

(3)針對這種情況,JDK 1.8 中引入了紅黑樹(查詢時間複雜度為 O(logn))來優化這個問題

二、JDK1.8中HashMap的資料結構

2.1HashMap是陣列+連結串列+紅黑樹(JDK1.8增加了紅黑樹部分)實現的

hashMap記憶體結構圖

新增紅黑樹
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent;  // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev;    // needed to unlink next upon deletion
boolean red; }

2.2HashMap 中關於紅黑樹的三個關鍵引數

TREEIFY_THRESHOLD

一個桶的樹化閾值

UNTREEIFY_THRESHOLD

一個樹的連結串列還原閾值

MIN_TREEIFY_CAPACITY

雜湊表的最小樹形化容量
static final int TREEIFY_THRESHOLD = 8
 static final int UNTREEIFY_THRESHOLD = 6
static final int MIN_TREEIFY_CAPACITY = 64
當桶中元素個數超過這個值時
需要使用紅黑樹節點替換連結串列節點
當擴容時,桶中元素個數小於這個值
就會把樹形的桶元素 還原(切分)為連結串列結構
當雜湊表中的容量大於這個值時,表中的桶才能進行樹形化
否則桶內元素太多時會擴容,而不是樹形化
為了避免進行擴容、樹形化選擇的衝突,這個值不能小於 4 * TREEIFY_THRESHOLD

2.3HashMap 在 JDK 1.8 中新增的操作:桶的樹形化 treeifyBin()

在Java 8 中,如果一個桶中的元素個數超過 TREEIFY_THRESHOLD(預設是 8 ),就使用紅黑樹來替換連結串列,從而提高速度。

這個替換的方法叫 treeifyBin() 即樹形化。

//將桶內所有的 連結串列節點 替換成 紅黑樹節點 1 final void treeifyBin(Node<K,V>[] tab, int hash) { 2   int n, index; Node<K,V> e; 3    //如果當前雜湊表為空,或者雜湊表中元素的個數小於 進行樹形化的閾值(預設為 64),就去新建/擴容 4   if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) 5        resize(); 6    else if ((e = tab[index = (n - 1) & hash]) != null) { 7        //如果雜湊表中的元素個數超過了 樹形化閾值,進行樹形化 8        // e 是雜湊表中指定位置桶裡的連結串列節點,從第一個開始 9        TreeNode<K,V> hd = null, tl = null//紅黑樹的頭、尾節點 10        do { 11            //新建一個樹形節點,內容和當前連結串列節點 e 一致 12            TreeNode<K,V> p = replacementTreeNode(e, null); 13            if (tl == null//確定樹頭節點 14                hd = p; 15           else { 16               p.prev = tl; 17                tl.next = p; 18            } 19            tl = p; 20        while ((e = e.next) != null);  21        //讓桶的第一個元素指向新建的紅黑樹頭結點,以後這個桶裡的元素就是紅黑樹而不是連結串列了 22        if ((tab[index] = hd) != null) 23            hd.treeify(tab); 24    }