深入分析hashmap
阿新 • • 發佈:2019-01-17
一、傳統 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增加了紅黑樹部分)實現的
新增紅黑樹
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 UNTREEIFY_THRESHOLD = 6 |
|
|
|
|
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 }
|