原文
主題 原始碼分析
紅黑樹注:以下原始碼基於jdk1.7.0_11
之前介紹了一系列map集合中的具體實現類,包括hashmap,hashtable,linkedhashmap。這三個類都是基於雜湊表實現的,今天我們介紹另一種map集合,treemap。treemap是基於紅黑樹實現的。
介紹treemap之前,回顧下紅黑樹的性質:
首先,我們要明確,紅黑樹是一種二叉排序樹,而且是平衡二叉樹。因而紅黑樹具有排序樹的所有特點,任意結點的左子樹(如果有的話)的值比該結點小,右子樹(如果有的話)的值比該結點大。二叉排序樹各項操作的平均時間複雜度為o(logn),但是最壞情況下,二叉排序樹會退化成單鏈表,此時時間複雜度為o(n),紅黑樹在二叉排序樹的基礎上,對其增加了一系列約束,使得其盡可能地平衡, 紅黑樹的查詢和更新的時間複雜度為o(logn)。
紅黑樹的五條性質:
1.每個結點要麼是紅色,要麼是黑色;
2.根結點為黑色;
3.葉結點為黑色(空結點);
4.若乙個結點為紅色,則其子結點為黑色;
5.每個葉結點到根結點的路徑中黑色結點的數目一致(黑高度相同)。
紅黑樹的查詢操作與二叉排序樹相同,重點是其插入和刪除操作。紅黑樹的插入和刪除操作在二叉排序樹的基礎上增加了修復操作,因為插入和刪除可能會導致樹不再滿足紅黑樹性質,這時候會通過著色、旋轉操作對其進行修復。
下面來看treemap的實現。
類宣告:
public class treemaptreemap同樣繼承abstractmap,但是它實現了n**igablemap介面,而n**igablemap介面繼承自sortedmap介面。extends abstractmap
implements n**igablemap, cloneable, j**a.io.serializable
treemap有四個成員變數,其中root是紅黑樹的根結點, 由於紅黑樹的查詢和更新操作需要比較,故而有個比較器comparator,預設情況下,comparator為空,這就要求我們的鍵必須實現comparable介面,以定義比較規則。
private final comparator super k> comparator;//比較器構造器:private transient entryroot = null;//樹根
/*** the number of entries in the tree
*/private transient int size = 0;//大小
/*** the number of structural modifications to the tree.
*/private transient int modcount = 0;//修改次數
public treemap()在檢視treemap的查詢和更新操作之前,我們先看下entry的實現,其實我們都可以猜到,entry既然是treemap儲存的結點,那麼其必然包括如下幾個域:資料(鍵、值)、父結點、左孩子、右孩子、顏色。public treemap(comparator super k> comparator)
public treemap(map extends k, ? extends v> m)
public treemap(sortedmapm)
}
事實正是如此:
static final class entry implements map.entry child links, and black color.下面來看put方法:*/entry(k key, v value, entryparent)
public k getkey()
public v getvalue()
public v setvalue(v value)
public boolean equals(object o)
public int hashcode()
public string tostring()
}
public v put(k key, v value)明確以下幾點:int cmp;
entryparent;//父結點
// split comparator and comparable paths
comparator super k> cpr = comparator;
if (cpr != null) while (t != null);
} else while (t != null);
} //找到插入點之後,建立新結點,插入之。
entrye = new entry<>(key, value, parent);
if (cmp < 0)//判斷是掛到左邊還是右邊
parent.left = e;
else
parent.right = e;
fixafterinsertion(e);//進行著色和旋轉等操作修復紅黑樹
size++;
modcount++;
return null;
}
1.treemap的查詢和更新操作都涉及到比較操作,故而treemap的鍵必須實現comparable介面或者構造時得傳入比較器(既實現了comparable介面又傳入了比較器情況下,比較器優先);
2.put操作不允許null鍵,但是值(value)允許為null;
3.鍵重複的情況下,新值會覆蓋掉舊值。
再看get方法:
public v get(object key)呼叫getentry方法查詢指定鍵值:
final entrygetentry(object key)對比hashmap近乎o(1)的查詢複雜度,treemap顯得略有不足。return null;//沒找到
} final entrygetentryusingcomparator(object key)
} return null;
}
再看remove刪除操作:
public v remove(object key)雖然看上去寥寥幾行**,其實邏輯十分複雜,具體體現在刪除結點後的恢復操作。
private void deleteentry(entryp) // p has 2 childrensuccesstor函式用於找乙個結點的中序後繼(參見之前寫的一篇如何得到乙個結點的中序後繼,演算法一致)://下面操作將釋放s結點,並修復紅黑樹
// start fixup at replacement node, if it exists.
entryreplacement = (p.left != null ? p.left : p.right);
if (replacement != null) else if (p.parent == null) else
} }
迭代器遍歷操作正是基於successtor操作完成的。所以遍歷treemap得到的鍵值對是有序的。
static treemap.entrysuccessor(entryt) else對稱地,還有個查詢直接前驅的函式:return p;
} }
static entrypredecessor(entryt) else注:文章故意忽略了更新操作中涉及到的紅黑樹修復動作(著色,旋轉),此部分內容較為複雜,作者目前也沒有完全吃透。return p;
} }
總結:1.treemap的實現基於紅黑樹;
2.treemap不允許插入null鍵,但允許null值;
3.treemap執行緒不安全;
4.插入結點時,若鍵重複,則新值會覆蓋舊值;
5.treemap要求key必須實現comparable介面,或者初始化時傳入comparator比較器;
6.遍歷treemap得到的結果集是有序的(中序遍歷);
7.treemap的各項操作的平均時間複雜度為o(logn).
TreeMap 原始碼分析
treemap底層是使用紅黑樹實現的儲存鍵值對的map容器,可以通過比較器進行排序。紅黑樹本質上是一棵弱平衡二叉樹,它的節點有紅色和黑色兩種顏色。主要特性有五點。1 每個節點或者是黑色,或者是紅色。2 根節點是黑色。3 每個葉子節點 nil 是黑色。注意 這裡葉子節點,是指為空 nil或null 的...
TreeMap原始碼分析
public v put k key,v value int cmp entryparent split comparator and comparable paths comparator cpr comparator if cpr null while t null else while t n...
TreeMap的原始碼
目錄 元素結構treemapentry 存放元素的邏輯 獲取元素 static final class treemapentryimplements map.entrypublic v put k key,v value int cmp treemapentryparent comparator c...