一、概要
- TreeMap是一個存儲鍵值對對象的集合,鍵值對對象表現為<Key,Value>,所有的Map集合保存的數據都是鍵值對集合。其中Key是關鍵字,不能重復。可以為null,但是null需要有比較器,并且比較器內部需要對null值進行處理,否則會報空指針異常,key自帶比較功能都不行。
- TreeMap必須有比較器,或者key自身自帶有比較功能,否則會報java.lang.ClassCastException
- 底層數據結構:Java中的底層數據結構是紅黑樹,Android中的java代碼使用的底層數據結構是平衡二叉查找樹,都可以成為數據結構是二叉樹。
- 添加、刪除、查找的時間復雜度是O(logn),就是單位時間的操作是的時間成本減少了一半。即2o = n ,那么o=logn。
- 實現了接口:NavigableMap,Cloneable,Serializable,其中NavigableMap又實現了接口SortedMap,在Android中TreeMap又直接實現了SortedMap。繼承了類AbstractMap。
- 源碼參考的Android-23和Java 1.8
二、構造函數
Java
一共三個構造函數
- 默認構造函數:將比較器設為空
- 參數是比較器子類的構造函數:將比較器聲明為參數中的比較器
- 參數是Map的構造函數:將比較器設為空,將參數map中的數據加入到該類中。
private final Comparator<? super K> comparator;//比較器
private transient Entry<K,V> root;//根節點
private transient int size = 0;//集合大小
private transient int modCount = 0;//修改次數
public TreeMap() {
comparator = null;
}
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
// Red-black mechanics,紅黑機械師手套????
private static final boolean RED = false;
private static final boolean BLACK = true;
//鍵值對對象:繼承自Map的鍵值對對象
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;//鍵
V value;//值
Entry<K,V> left;//左子樹節點
Entry<K,V> right;//右子樹節點
Entry<K,V> parent;//父節點
boolean color = BLACK;//顏色,黑色為true
/**
* Make a new cell with given key, value, and parent, and with
* {@code null} child links, and BLACK color.
*/
Entry(K key, V value, Entry<K,V> parent) {//創建一個鍵值對對象
this.key = key;
this.value = value;
this.parent = parent;
}
/**
* Returns the key.
* 獲取該對象的鍵
* @return the key
*/
public K getKey() {
return key;
}
/**
* Returns the value associated with the key.
* 獲取該對象的值
* @return the value associated with the key
*/
public V getValue() {
return value;
}
/**
* Replaces the value currently associated with the key with the given
* value.
* 設置該對象的值
* @return the value associated with the key before this method was
* called
*/
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
//判斷鍵值對是否相等
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
//判斷鍵相等和值相等
return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}
//獲取鍵值對的哈希值
public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
//轉換成string
public String toString() {
return key + "=" + value;
}
}
//相等方法
static final boolean valEquals(Object o1, Object o2) {
return (o1==null ? o2==null : o1.equals(o2));
}
Android
一共四個構造函數
- 默認構造函數:將比較器設為自定義的默認比較器
- 參數是Map的構造函數:調用默認構造函數,循環將map中的數據循環插入到該對象中
- 參數是Compartor的構造函數:如果參數不為null,就將比較器設為參數中比較器,如果為null就將比較器設為默認的比較器
- 參數是SortedMap的構造函數:將比較器設為SortedMap的比較器(不為空),為空仍然設為默認的比較器。將SortedMap中的數據循環插入到該對象中
@SuppressWarnings("unchecked") // to avoid //Comparable<Comparable<Comparable<...>>>,
//自定義默認比較器,用于比較,該比較器限定比較的兩個對象都是Comparable的
private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
public int compare(Comparable a, Comparable b) {
return a.compareTo(b);
}
};
Comparator<? super K> comparator;//比較器
Node<K, V> root;//根節點
int size = 0;//大小
int modCount = 0;//修改次數
public TreeMap() {
//默認比較器的泛型是Comparable,所以需要限定k是Comparable的子類
this.comparator = (Comparator<? super K>) NATURAL_ORDER;
}
public TreeMap(Map<? extends K, ? extends V> copyFrom) {
this();
for (Map.Entry<? extends K, ? extends V> entry : copyFrom.entrySet()) {
putInternal(entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked") // unsafe! if comparator is null, this assumes K is comparable
public TreeMap(Comparator<? super K> comparator) {
if (comparator != null) {
this.comparator = comparator;
} else {
this.comparator = (Comparator<? super K>) NATURAL_ORDER;
}
}
@SuppressWarnings("unchecked") // if copyFrom's keys are comparable this map's keys must be also
public TreeMap(SortedMap<K, ? extends V> copyFrom) {
Comparator<? super K> sourceComparator = copyFrom.comparator();
if (sourceComparator != null) {
this.comparator = sourceComparator;
} else {
this.comparator = (Comparator<? super K>) NATURAL_ORDER;
}
for (Map.Entry<K, ? extends V> entry : copyFrom.entrySet()) {
putInternal(entry.getKey(), entry.getValue());//具體解析參考增加數據
}
}
static class Node<K, V> implements Map.Entry<K, V> {
Node<K, V> parent;//父節點
Node<K, V> left;//左子節點
Node<K, V> right;//右子節點
final K key;//KEY
V value;//值
int height;//高度,判斷平衡的標識
Node(Node<K, V> parent, K key) {
this.parent = parent;
this.key = key;
this.height = 1;
}
//node的copy,重新copy一個新的樹,主要用于TreeMap的Clone
Node<K, V> copy(Node<K, V> parent) {
//創建一個新節點
Node<K, V> result = new Node<K, V>(parent, key);
if (left != null) {
result.left = left.copy(result);//copy左子樹
}
if (right != null) {
result.right = right.copy(result);//copy右子樹
}
result.value = value;//值賦值
result.height = height;//高度賦值
return result;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
@Override//判斷節點是否相等
public boolean equals(Object o) {
if (o instanceof Map.Entry) {
Map.Entry other = (Map.Entry) o;
//相等的判斷是鍵相等和值相等
return (key == null ? other.getKey() == null : key.equals(other.getKey()))
&& (value == null ? other.getValue() == null : value.equals(other.getValue()));
}
return false;
}
@Override
public int hashCode() {//返回節點的哈希值
return (key == null ? 0 : key.hashCode())
^ (value == null ? 0 : value.hashCode());
}
@Override
public String toString() {//轉換成String
return key + "=" + value;
}
/**
* Returns the next node in an inorder traversal, or null if this is the
* last node in the tree.
* 在序列中該節點后面的一個節點,該序列按照比較器給定順序排序
* 如果樹的根節點左子樹上節點比根小,那么就返回序列中該節點后面的第一個節點。該序列指的是樹中所有節點按照從小到大順序排列。
*/
Node<K, V> next() {
if (right != null) {
return right.first();
}
Node<K, V> node = this;
Node<K, V> parent = node.parent;
while (parent != null) {
if (parent.left == node) {
return parent;
}
node = parent;
parent = node.parent;
}
return null;
}
/**
* Returns the previous node in an inorder traversal, or null if this is
* the first node in the tree.
* 在序列中該節點前面的一個節點,該序列按照比較器給定順序排序
* 如果樹的根節點左子樹上節點比根小,那么就返回序列中該節點前面的一個節點。該序列指的是樹中所有節點按照從小到大順序排列。
*/
public Node<K, V> prev() {
if (left != null) {
return left.last();
}
Node<K, V> node = this;
Node<K, V> parent = node.parent;
while (parent != null) {
if (parent.right == node) {
return parent;
}
node = parent;
parent = node.parent;
}
return null;
}
/**
* Returns the first node in this subtree.
* 在序列中的第一個節點,該序列按照比較器給定順序排序
* 如果樹的根節點左子樹上節點比根小,那么就返回序列中第一個節點,就是樹中最小的節點。該序列指的是樹中所有節點按照從小到大順序排列。
*/
public Node<K, V> first() {
Node<K, V> node = this;
Node<K, V> child = node.left;
while (child != null) {
node = child;
child = node.left;
}
return node;
}
/**
* Returns the last node in this subtree.
* 在序列中的最后一個節點,該序列按照比較器給定順序排序
* 如果樹的根節點左子樹上節點比根小,那么就返回序列中最后一個節點,就是樹中最大的節點。該序列指的是樹中所有節點按照從小到大順序排列。
*/
public Node<K, V> last() {
Node<K, V> node = this;
Node<K, V> child = node.right;
while (child != null) {
node = child;
child = node.right;
}
return node;
}
}
三、增加元素
4.1 增加一個元素
Java
紅黑樹增加,增加完成一個元素,如果破壞了紅黑樹特性就需要修復紅黑樹
關于紅黑樹的處理可以參考:鏈接
紅黑樹的葉子節點全部是黑色NULL節點,新插入的節點在NULL的上一層,新插入的節點默認是紅色。
增加的核心點在于紅黑樹破壞之后的修復,也就是重新維持平衡的過程。
維持平衡操作的核心是自底向上循環往復判斷處理破壞平衡的點,直到根節點為止。
該方法既是增加也是修改。
public V put(K key, V value) {
Entry<K,V> t = root;//臨時節點
if (t == null) {//根節點為空
compare(key, key); // type (and possibly null) check,key為空判斷,需要在比較器中處理,如果沒有比較器且key不是自己實現了比較器,那么就會報空指針異常。
root = new Entry<>(key, value, null);//創建根節點
size = 1;//將size設為1
modCount++;//修改次數增加1,迭代器使用
return null;
}
int cmp;//比較值
Entry<K,V> parent;//父節點
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;//比較器
//查找父jie'dian
if (cpr != null) {//比較器不為空
do {
parent = t;//父節點指向臨時節點
cmp = cpr.compare(key, t.key);//比較臨時t節點
if (cmp < 0)//比臨時節點小,就向臨時節點的左子樹查找
t = t.left;
else if (cmp > 0)//比臨時節點大,就向臨時節點的右子樹查找
t = t.right;
else//和臨時節點相等,就表示找到了節點,將節點內值修改掉
return t.setValue(value);
} while (t != null);
//如果臨時節點不為空,就一直向下查找,直到找到一個空節點,此時父節點指向了上一個不為空的節點。
}else {//TreeMap不包含比較器,那么就是自己實現了比較功能
if (key == null)//如果key為空,拋出空指針異常
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;//檢查k是否實現了比較功能,如果沒實現會報空指針異常。
do {
parent = t;//父節點指向臨時節點k
cmp = k.compareTo(t.key);//key自己比較
//該方法解析同上,只是
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);//修改完成直接返回老的值
} while (t != null);
}
//創建key-value鍵值對節點,parent指向該節點的父節點
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)//小于0表示新節點是父節點的左子
parent.left = e;
else//大于0表示新節點是父節點的右子
parent.right = e;
fixAfterInsertion(e);//新增完成修復節點
size++;//大小增加
modCount++;//修改數量增加
return null;//返回null表示新增。
}
final int compare(Object k1, Object k2) {//比較key值
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
/** From CLR */
//CLR==Common Language Runtime????
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;//將新節點顏色涂成紅色,默認約定,方便平衡紅黑樹
//當破壞平衡節點不為空,破壞平衡節點不是根節點,該節點的父節點為紅色
while (x != null && x != root && x.parent.color == RED) {
//連續的兩個紅節點,違背了紅黑樹的性質,關于紅黑樹,可以先看看紅黑樹相關文章
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//父節點是祖父節點的左子
Entry<K,V> y = rightOf(parentOf(parentOf(x)));//獲得叔叔節點
if (colorOf(y) == RED) {//叔叔節點是紅色
setColor(parentOf(x), BLACK);//將父節點設為黑色
setColor(y, BLACK);//將叔叔節點設為紅色
setColor(parentOf(parentOf(x)), RED);//將祖父節點設為黑色,就是交換了祖父節點的
x = parentOf(parentOf(x));//將祖父節點設為新的破壞平衡的節點
} else {//叔叔節點是黑色
if (x == rightOf(parentOf(x))) {//當前節點是父節點的右子,左內側插入
x = parentOf(x);//將節點設為父節點
rotateLeft(x);//左旋,此時相當于交換了父節點和子節點的順序
}
setColor(parentOf(x), BLACK);//設置父節點為黑色
setColor(parentOf(parentOf(x)), RED);//設置祖父節點為紅色
rotateRight(parentOf(parentOf(x)));//以祖父節點為旋轉點右旋
//判斷新子樹的根節點是否影響了整棵樹的平衡。
}
} else {//父節點是祖父節點的右子
Entry<K,V> y = leftOf(parentOf(parentOf(x)));//獲取叔叔節點
if (colorOf(y) == RED) {//叔叔節點顏色為紅色
setColor(parentOf(x), BLACK);//將父節點顏色設為黑色
setColor(y, BLACK);//將叔叔節點顏色設為黑色
setColor(parentOf(parentOf(x)), RED);//將祖父節點設為黑色
x = parentOf(parentOf(x));//將節點設為祖父節點,用于判斷祖父節點是否破壞平衡。
} else {//叔叔節點的顏色是黑色
if (x == leftOf(parentOf(x))) {//當前節點是父節點的左子,右內側插入
x = parentOf(x);//將節點指向父節點,此時就是將福界定啊
rotateRight(x);//右旋轉
}
//變成右外側插入
setColor(parentOf(x), BLACK);//將父節點顏色設為黑色
setColor(parentOf(parentOf(x)), RED);//將祖父節點設為紅
rotateLeft(parentOf(parentOf(x)));//祖父節點左旋
//此時這個子樹的根節點變為了--父節點--,判斷該子樹是否影響了整個樹的平衡
}
}
}
root.color = BLACK;
}
/**
* Balancing operations.//平衡相關操作
*
* Implementations of rebalancings during insertion and deletion are
* slightly different than the CLR version. Rather than using dummy
* nilnodes, we use a set of accessors that deal properly with null. They
* are used to avoid messiness surrounding nullness checks in the main
* algorithms.
*/
//獲取節點的顏色,如果為空返回黑色
private static <K,V> boolean colorOf(Entry<K,V> p) {
return (p == null ? BLACK : p.color);
}
//獲取當前節點的父節點
private static <K,V> Entry<K,V> parentOf(Entry<K,V> p) {
return (p == null ? null: p.parent);
}
//設置當前節點的顏色
private static <K,V> void setColor(Entry<K,V> p, boolean c) {
if (p != null)
p.color = c;
}
//獲取當前節點的左子節點
private static <K,V> Entry<K,V> leftOf(Entry<K,V> p) {
return (p == null) ? null: p.left;
}
//獲取當前節點的右子節點
private static <K,V> Entry<K,V> rightOf(Entry<K,V> p) {
return (p == null) ? null: p.right;
}
/** From CLR */
//左旋操作,具體參照圖左旋,核心點先處理p的右子樹,然后處理右子樹的左子,后處理父節點,左節點不處理
private void rotateLeft(Entry<K,V> p) {
if (p != null) {//節點不為空,旋轉才有意義
Entry<K,V> r = p.right;//記錄右節點
p.right = r.left;//將p節點的右子,設為r的左子
if (r.left != null)//如果r的左子存在,就將r的左子父節點設為p
r.left.parent = p;
r.parent = p.parent;//r的父節點設為p的父節點
if (p.parent == null)//如果p的父節點為null,表示p為root節點
root = r;//將root節點設為r
else if (p.parent.left == p)//如果p是p的父節點的左子,將p的父的左子設為r
p.parent.left = r;
else//不是,那么p就是p的父節點的右子,將p的右子設為r
p.parent.right = r;
r.left = p;//r的左子設為p
p.parent = r;//p的父節點設為r
}
}
/** From CLR */
//左旋操作,具體參照圖右旋,核心點先處理p的左子樹,然后左子樹的右子,后處理父節點,左子節點不處理
private void rotateRight(Entry<K,V> p) {
if (p != null) {
Entry<K,V> l = p.left;//記錄左子樹
p.left = l.right;//將p的左子設為左子樹的左子
if (l.right != null) l.right.parent = p;//如果p的左子樹的右子不為空,將左子樹右子的父節點設為當前節點
l.parent = p.parent;//將左子樹的父節點設為p的父節點
if (p.parent == null)//如果p的父節點為空,表示p是根節點
root = l;//旋轉后的根節點就是L
else if (p.parent.right == p)//如果p是父節點的右子就將父節點的右子設為L
p.parent.right = l;
else p.parent.left = l;//如果p是父節點的左子就將父節點的做子設為L
l.right = p;//將l的右子設為p
p.parent = l;//將p的父節點設為l
}
}
左旋和右旋如下圖所示
Android
底層的數據結構是平衡二叉樹。在節點高度差超過1的時候表示破壞了平衡,此時需要處理破壞平衡的節點。
處理方法的核心就是循環向上查找,直到找到根節點或者平衡節點。
左右旋轉可以參考上圖,除了代碼不太一致,核心點還是相同的。
@Override
public V put(K key, V value) {
return putInternal(key, value);
}
V putInternal(K key, V value) {
//查找,如果找不到就創建一個節點
Node<K, V> created = find(key, Relation.CREATE);
//找到節點的值,此時值有可能為null,如新創建的節點
V result = created.value;
created.value = value;//將節點值設為新值
return result;//將老值返回,
}
//根據關系查找節點
Node<K, V> find(K key, Relation relation) {
if (root == null) {//表示樹中沒有節點
//判斷是否有比較器,獲取key實現比較結構
if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
throw new ClassCastException(key.getClass().getName() + " is not Comparable"); // NullPointerException ok
}
if (relation == Relation.CREATE) {//關系等于創建時
root = new Node<K, V>(null, key);//創建root節點
size = 1;
modCount++;
return root;//將root節點返回
} else {
return null;
}
}
/*
* Micro-optimization: avoid polymorphic calls to Comparator.compare().
* This is 10% faster for naturally ordered trees.
* 檢查比較器是否存在,或者key自帶比較功能
*/
@SuppressWarnings("unchecked") // will throw a ClassCastException below if there's trouble
Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
? (Comparable<Object>) key
: null;
Node<K, V> nearest = root;//將最近的值設為
while (true) {
int comparison = (comparableKey != null)
? comparableKey.compareTo(nearest.key)
: comparator.compare(key, nearest.key);
/*
* We found the requested key.,找到需要的節點
*/
if (comparison == 0) {
switch (relation) {
case LOWER://查找比當前節點低的節點,返回前一個小的節點
return nearest.prev();
case FLOOR://地板,或者叫向下取整,如果相等就是它自己
case EQUAL://相等關系的節點
case CREATE://創建的節點,如果是創建表示表示找到了,不用創建
case CEILING://天花板,或者叫向上取整,如果相等了,表示就是自己
return nearest;//返回找到的節點
case HIGHER://更大的節點,當前節點的下一個節點
return nearest.next();
}
}
//如果小于0,接著向左子樹找,如果大于0接著向右子樹找
Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
if (child != null) {//如果child不為空表示沒有找到葉子節點。
nearest = child;
continue;
}
/*
* We found a nearest node. Every key not in the tree has up to two
* nearest nodes, one lower and one higher.
* 找到了一個相近的節點,沒有找到key值相等的節點
*/
//表示需要的節點更小,比找到的節點小。找到的節點稍大
if (comparison < 0) { // nearest.key is higher
switch (relation) {
case LOWER://更小的節點,
case FLOOR://地板上節點
return nearest.prev();//將小一點的節點返回即可
case CEILING://天花板上的節點
case HIGHER://更大一點的節點
return nearest;//將找到的節點返回即可
case EQUAL://沒有相等的節點
return null;
case CREATE://創建一個節點
Node<K, V> created = new Node<K, V>(nearest, key);
nearest.left = created;//創建節點是比找到的節點小
size++;
modCount++;
rebalance(nearest, true);//平衡節點,判斷新創建的節點是否破壞平衡
return created;//將創建節點返回
}
} else { // comparison > 0, nearest.key is lower
//找到的節點比要找的節點稍小,但是沒有要找的節點
switch (relation) {
case LOWER://更小一點的節點
case FLOOR://地板上的的節點
return nearest;//將找到的節點返回
case CEILING://天花板上的節點
case HIGHER://更大一點的節點
return nearest.next();//返回找到的節點的后續節點
case EQUAL://沒有相等的節點
return null;
case CREATE://如果創建,就創建一個節點
Node<K, V> created = new Node<K, V>(nearest, key);
nearest.right = created;//將創建的節點設為找到節點的右節點
size++;
modCount++;
rebalance(nearest, true);//平衡樹
return created;//返回創建節點
}
}
}
}
//傳入的是不平衡點,是否是插入狀態,因為有可能是刪除
private void rebalance(Node<K, V> unbalanced, boolean insert) {
//賦值不平衡點,判斷不平衡點是否為空.
for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
Node<K, V> left = node.left;//獲取不平衡點的左子節點
Node<K, V> right = node.right;//獲取不平衡點的右子節點
int leftHeight = left != null ? left.height : 0;//獲取左子的高度
int rightHeight = right != null ? right.height : 0;//獲取右子的高度
int delta = leftHeight - rightHeight;//計算兩者的高度差
//如果是插入節點,那么高度差一般為0或者為+-1
//高度差為+-2的情況一般是第一層循環產生之后
if (delta == -2) {//右子樹比左子樹高
Node<K, V> rightLeft = right.left;//記錄右子樹的左子
Node<K, V> rightRight = right.right;//記錄右子樹的右子
int rightRightHeight = rightRight != null ? rightRight.height : 0;//右子樹右子
int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;//右子樹左子
int rightDelta = rightLeftHeight - rightRightHeight;//左子高度-右子高度
//rightDelta==-1表示右右
//rightDelta == 0 && !insert,表示刪除前右邊高,還是右右,需要左旋
if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
rotateLeft(node); // AVL right right
} else {//這種情況是右左,先右旋變成右右,然后左旋變成左右
// assert (rightDelta == 1);
rotateRight(right); // AVL right left
rotateLeft(node);
}
if (insert) {//如果是插入,旋轉之后高度差已經減少了,可以直接跳出循環
break; // no further rotations will be necessary
}
} else if (delta == 2) {//與上面的完全相反
Node<K, V> leftLeft = left.left;
Node<K, V> leftRight = left.right;
int leftRightHeight = leftRight != null ? leftRight.height : 0;
int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
//計算左子-右子的高度差
int leftDelta = leftLeftHeight - leftRightHeight;
//左左需要右旋
if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
rotateRight(node); // AVL left left
} else {//左右,先左旋后右旋
// assert (leftDelta == -1);
rotateLeft(left); // AVL left right
rotateRight(node);
}
if (insert) {
break; // no further rotations will be necessary
}
} else if (delta == 0) {
//左子樹節點和右子樹節點高度相等,其實這個節點的高度并沒有變
node.height = leftHeight + 1; // leftHeight == rightHeight
if (insert) {//如果是插入的話,這個時候已經平衡了,如果是刪除有可能導致父節點不平衡,極限循環
break; // the insert caused balance, so rebalancing is done!
}
} else {
// assert (delta == -1 || delta == 1);
//如果高度差為+-1,插入時表示插入在該節點下插入了一個節點,而這個節點原來均為空,那么需要判斷這個節點的父節點是否平衡,因為父節點有可能只有一個子節點,導致高度差大于1
// 如果是刪除的情況,那么刪除一個子節點,那么刪除后可能會導致父節點兩個子樹高度差變為2,所以刪除后需要進行循環
node.height = Math.max(leftHeight, rightHeight) + 1;
if (!insert) {
break; // the height hasn't changed, so rebalancing is done!
}
}
}
}
/**
* Rotates the subtree so that its root's right child is the new root.
*/
private void rotateLeft(Node<K, V> root) {
Node<K, V> left = root.left;//樹的左節點
Node<K, V> pivot = root.right;//樹的右節點
Node<K, V> pivotLeft = pivot.left;//樹的右節點的左節點,右左節點
Node<K, V> pivotRight = pivot.right;//樹的右節點的有節點,記錄為右右節點
// move the pivot's left child to the root's right
root.right = pivotLeft;//將根節點的右節點右左節點
if (pivotLeft != null) {//如果右左不為空
pivotLeft.parent = root;//右左節點的父節點改為跟節點
}
//將右節點的父節點修改root的父節點,然后將父節點的子節點修改右節點
replaceInParent(root, pivot);
// move the root to the pivot's left
pivot.left = root;//右節點的左節點指向原跟節點
root.parent = pivot;//原根節點的父節點指向右節點
// fix heights,重新修改兩個節點的高度
root.height = Math.max(left != null ? left.height : 0,
pivotLeft != null ? pivotLeft.height : 0) + 1;
pivot.height = Math.max(root.height,
pivotRight != null ? pivotRight.height : 0) + 1;
}
/**
* Rotates the subtree so that its root's left child is the new root.
* 根節點也是旋轉點
*/
private void rotateRight(Node<K, V> root) {
Node<K, V> pivot = root.left;//記錄左節點
Node<K, V> right = root.right;//記錄右節點
Node<K, V> pivotLeft = pivot.left;//左節點的左節點,記為左左節點
Node<K, V> pivotRight = pivot.right;//左節點的右節點,記為左右節點
// move the pivot's right child to the root's left
root.left = pivotRight;//將根的左節點修改為左右節點
if (pivotRight != null) {//如果左右節點不為空
pivotRight.parent = root;//將左右節點父節點指向根節點
}
//替換父節點,將左節點的父節點修改原根節點的父節點,將父節點的子節點修改左節點
replaceInParent(root, pivot);
// move the root to the pivot's right
pivot.right = root;//將左節點的右子節點修改為根節點(旋轉節點)
root.parent = pivot;//將旋轉節點的父節點修改為左節點,這樣原左節點就變成了新的根節點
// fixup heights,修改變化的兩個節點的高度
root.height = Math.max(right != null ? right.height : 0,
pivotRight != null ? pivotRight.height : 0) + 1;
pivot.height = Math.max(root.height,
pivotLeft != null ? pivotLeft.height : 0) + 1;
}
//替換與父節點的鏈接關系
private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
Node<K, V> parent = node.parent;//記錄node節點的父節點
node.parent = null;//將node指向父節點的對象置空
if (replacement != null) {//如果替換節點不為空
replacement.parent = parent;//將替換節點的父節點設為新的節點
}
if (parent != null) {//如果父節點不為空
if (parent.left == node) {//node節點的左子是node,就將左子設為替換節點
parent.left = replacement;
} else {//node節點的右子是node,就將右子設為替換節點
// assert (parent.right == node);
parent.right = replacement;
}
} else {//父節點為空,直接將根節點指向替換節點
root = replacement;
}
}
4.2 批量增加集合中的元素
Java
如果說map是SortedMap,且當前的TreeMap為空,參數map的比較器和當前TreeMap的比較器相同,那么使用迭代器從小到大以折半的方式將樹組裝成一個新的紅黑樹,而且最下方的節點是紅色,其他的都是黑色。
如果不滿足上述條件,那么使用的一個個插入的方式。
因為一個插入需要修復紅黑樹,性能上比較差。
public void putAll(Map<? extends K, ? extends V> map) {
int mapSize = map.size();
//當前集合數據為空,并且插入的集合不為空,并且插入的集合是排序集合
if (size==0 && mapSize!=0 && map instanceof SortedMap) {
//獲取map的比較器
Comparator<?> c = ((SortedMap<?,?>)map).comparator();
//比較器相等,==或者equals。地址相等表示就是一個,equals表示地址之外相等
if (c == comparator || (c != null && c.equals(comparator))) {
++modCount;
try {
//將size和Key迭代器傳入,關于迭代器可以看本篇中的迭代器代碼
buildFromSorted(mapSize, map.entrySet().iterator(),
null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
return;
}
}
super.putAll(map);
}
/**
* Linear time tree building algorithm from sorted data. Can accept keys
* and/or values from iterator or stream. This leads to too many
* parameters, but seems better than alternatives. The four formats
* that this method accepts are:
*
* 1) An iterator of Map.Entries. (it != null, defaultVal == null).
* 2) An iterator of keys. (it != null, defaultVal != null).
* 3) A stream of alternating serialized keys and values.
* (it == null, defaultVal == null).
* 4) A stream of serialized keys. (it == null, defaultVal != null).
*
* It is assumed that the comparator of the TreeMap is already set prior
* to calling this method.
*
* @param size the number of keys (or key-value pairs) to be read from
* the iterator or stream
* @param it If non-null, new entries are created from entries
* or keys read from this iterator.
* @param str If non-null, new entries are created from keys and
* possibly values read from this stream in serialized form.
* Exactly one of it and str should be non-null.
* @param defaultVal if non-null, this default value is used for
* each value in the map. If null, each value is read from
* iterator or stream, as described above.
* @throws java.io.IOException propagated from stream reads. This cannot
* occur if str is null.
* @throws ClassNotFoundException propagated from readObject.
* This cannot occur if str is null.
*/
private void buildFromSorted(int size, Iterator<?> it,
java.io.ObjectInputStream str,
V defaultVal)
throws java.io.IOException, ClassNotFoundException {
this.size = size;//修改當前的size為map的size
//折半查找,computeRedLevel計算紅色的level,最下面一級非葉子就是level
//TreeMap中的葉子節點是空節點
root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
it, str, defaultVal);
}
private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
int redLevel,
Iterator<?> it,
java.io.ObjectInputStream str,
V defaultVal)
throws java.io.IOException, ClassNotFoundException {
/*
* Strategy: The root is the middlemost element. To get to it, we
* have to first recursively construct the entire left subtree,
* so as to grab all of its elements. We can then proceed with right
* subtree.
*
* The lo and hi arguments are the minimum and maximum
* indices to pull out of the iterator or stream for current subtree.
* They are not actually indexed, we just proceed sequentially,
* ensuring that items are extracted in corresponding order.
*/
if (hi < lo) return null;
int mid = (lo + hi) >>> 1;
Entry<K,V> left = null;
if (lo < mid)//一直折半查找,記錄最小的值,關于折半的原因是二叉樹
left = buildFromSorted(level+1, lo, mid - 1, redLevel,
it, str, defaultVal);
// extract key and/or value from iterator or stream
//迭代器出來的值就排序上第一值
K key;
V value;
if (it != null) {
if (defaultVal==null) {
Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();//找到最小的節點
key = (K)entry.getKey();
value = (V)entry.getValue();
} else {
key = (K)it.next();
value = defaultVal;
}
} else { // use stream
key = (K) str.readObject();
value = (defaultVal != null ? defaultVal : (V) str.readObject());
}
//middle表示找到的位置,創建節點,在遞歸時這個節點有可能是左節點右節點
Entry<K,V> middle = new Entry<>(key, value, null);
// color nodes in non-full bottommost level red
if (level == redLevel)//如果level相等,將顏色設置為紅色
middle.color = RED;
//除了最下層的非葉子節點,那么所有的節點都是黑色的。
if (left != null) {
middle.left = left;
left.parent = middle;
}
if (mid < hi) {//查找右半部分
Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
it, str, defaultVal);
middle.right = right;
right.parent = middle;
}
return middle;
}
/**
* Find the level down to which to assign all nodes BLACK. This is the
* last `full' level of the complete binary tree produced by
* buildTree. The remaining nodes are colored RED. (This makes a `nice'
* set of color assignments wrt future insertions.) This level number is
* computed by finding the number of splits needed to reach the zeroeth
* node. (The answer is ~lg(N), but in any case must be computed by same
* quick O(lg(N)) loop.)
* 找到非葉子的level,二叉樹的最下面的葉子是紅色的。
*/
private static int computeRedLevel(int sz) {
int level = 0;
for (int m = sz - 1; m >= 0; m = m / 2 - 1)
level++;
return level;
}
AbstractMap.java
public void putAll(Map<? extends K, ? extends V> m) {
//循環調用put方法。
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
Android
直接調用的就是一個個插入的方式,可能是AVL樹的修復復雜度比較低,性能相對來說比較好.
AbstractMap.java
public void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
四、刪除元素
刪除元素一般指的是刪除一個元素
Java
找到一個元素,如果元素不為null,記錄元素的值,將元素刪除,返回記錄的值
刪除情況一共有6中情況,具體參考:紅黑樹
public V remove(Object key) {
Entry<K,V> p = getEntry(key);//獲取一個節點信息
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p);
return oldValue;
}
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)//如果比較器不為null,表示使用比較器的進行排序,需要另外的查找
return getEntryUsingComparator(key);
if (key == null)//在沒有比較器的情況下,key不能為null
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;//可比較的key
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);//比較
if (cmp < 0)//如果小于0,表示key值比較小
p = p.left;//向左找
else if (cmp > 0)//如果大于0,表示key值比較大
p = p.right;//向右找
else//如果相等,表示找到了,返回即可
return p;
}
return null;//循環跳出,并且沒有找到,表示沒有相應的key
}
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
// If strictly internal, copy successor's element to p and then make p
// point to successor.
// 如果刪除的節點有兩個子樹,就找到右子樹上的最小節點來替換掉該節點
// 并將刪除節點設為找到的節點。
if (p.left != null && p.right != null) {
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
// Start fixup at replacement node, if it exists.
// 如果需要刪除的節點,有左節點就用刪除節點的左節點當做替換節點
// 如果需要刪除的節點沒有左節點,就用右節點當做替換節點
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null) {//如果有替換節點,將替換節點替換掉刪除節點
// Link replacement to parent
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null;//將刪除節點刪除
// Fix replacement,如果刪除的節點是紅色,就直接刪掉即可,因為并沒有破壞紅黑樹。如果刪除的節點是黑色就需要修復紅黑樹,因為破壞了紅黑樹的性質
if (p.color == BLACK)
fixAfterDeletion(replacement);//將破壞平衡點傳入,修復紅黑樹
} else if (p.parent == null) { // return if we are the only node.
root = null;//如果刪除點沒有子節點,并且沒有父節點,那么刪除點就是根節點,那么刪除它
} else { // No children. Use self as phantom replacement and unlink.
if (p.color == BLACK)//如果刪除點是黑色,并且沒有子節點,那么以刪除點為修復點,重新修改紅黑樹。
fixAfterDeletion(p);
if (p.parent != null) {//如果有父節點,最后將自己刪除掉
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
/** From CLR */
private void fixAfterDeletion(Entry<K,V> x) {
//如果破壞平衡的節點不是根,并且節點顏色為黑色,那么表示根節點經過這個節點到達葉子節點路徑上黑色節點數和其他路徑的黑色節點上不同
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {//判斷該節點是否是父節點的左子樹
Entry<K,V> sib = rightOf(parentOf(x));//兄弟節點
if (colorOf(sib) == RED) {//兄弟節點為紅色,表達的是刪除情況3
setColor(sib, BLACK);//將兄弟節點涂成黑色
setColor(parentOf(x), RED);//將父節點涂成紅色
rotateLeft(parentOf(x));//以父節點為旋轉點,左旋
sib = rightOf(parentOf(x));//重新定位兄弟節點,新的兄弟節點肯定為黑色
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {//兄弟節點及其子節點為黑色,刪除情況4
setColor(sib, RED);//將兄弟節點涂成紅色
x = parentOf(x);//此時需要判斷父節點是否破壞平衡,依次向上遞歸
} else {//兄弟節點的子節點有一個是紅色
if (colorOf(rightOf(sib)) == BLACK) {//兄弟節點的右子有黑色,左子為紅色,刪除情況65
setColor(leftOf(sib), BLACK);//將兄弟節點的左節點涂成黑色
setColor(sib, RED);//將兄弟節點涂成紅
rotateRight(sib);//以兄弟節點為旋轉點,右旋
sib = rightOf(parentOf(x));//兄弟節點重新定位,應該是原兄弟節點的左子
}//兄弟節點右子為紅色,不考慮左子。刪除情況6
setColor(sib, colorOf(parentOf(x)));//將兄弟節點顏色設為父節點顏色
setColor(parentOf(x), BLACK);//將父節點設為黑色
setColor(rightOf(sib), BLACK);//將兄弟節點的右子設為黑色
rotateLeft(parentOf(x));//以根節點為旋轉點,左旋
x = root;//一般這種情況都平衡了,所以將x指向root,在情況4下跳出循環統一設置最后判斷點顏色。
}
} else { // symmetric,為上半部分算法,反向對稱
Entry<K,V> sib = leftOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
// 將最后的判斷點設為黑色,因為最后的判斷點有可能是根節點,或者是紅色但是需要黑色,這種時候設置為最后判斷點為黑色即可。
setColor(x, BLACK);
}
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
return null;
else if (t.right != null) {//查找右子樹上最小點
Entry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else {//在判斷是否包含值時使用,用于遍歷整個樹,這個判斷節點用于向根遞歸
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
Android
是AVL樹的刪除算法,平衡算法比較簡單,依次從破壞平衡點向根判斷是否平衡
@Override
public V remove(Object key) {
Node<K, V> node = removeInternalByKey(key);//刪除節點
return node != null ? node.value : null;//是否有刪除值
}
Node<K, V> removeInternalByKey(Object key) {
Node<K, V> node = findByObject(key);//找到這個節點
if (node != null) {//如果節點存在
removeInternal(node);//刪除它
}
return node;
}
Node<K, V> findByObject(Object key) {
return find((K) key, EQUAL);//在增加元素中,有該方法的具體說明
}
void removeInternal(Node<K, V> node) {
Node<K, V> left = node.left;//左子樹
Node<K, V> right = node.right;//右子樹
Node<K, V> originalParent = node.parent;//父節點
if (left != null && right != null) {//左右子樹不為空
/*
* To remove a node with both left and right subtrees, move an
* adjacent node from one of those subtrees into this node's place.
*
* Removing the adjacent node may change this node's subtrees. This
* node may no longer have two subtrees once the adjacent node is
* gone!
*/
// 如果左子樹高度比較高,那么選擇左子樹最大節點替換刪除節點
// 如果右子樹高度比較高或相等,那么選擇右子樹最小節點替換刪除節點
Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
//判斷替換節點是否破壞平衡,以及恢復平衡
removeInternal(adjacent); // takes care of rebalance and size--
int leftHeight = 0;
left = node.left;//重新定位left
if (left != null) {//如果左子不為空
leftHeight = left.height;//獲取左子高度
adjacent.left = left;//將左子設為替換節點的左子
left.parent = adjacent;//將左子的父節點設為新節點
node.left = null;//將node的左子置空
}
//相同方法處理右子樹
int rightHeight = 0;
right = node.right;
if (right != null) {
rightHeight = right.height;
adjacent.right = right;
right.parent = adjacent;
node.right = null;
}
//將替換節點的高度,重新設置
adjacent.height = Math.max(leftHeight, rightHeight) + 1;
//將node父節點指向的節點指向替換節點
replaceInParent(node, adjacent);
return;
} else if (left != null) {//左子樹不為空
replaceInParent(node, left);//以左子樹根節點替換刪除節點
node.left = null;
} else if (right != null) {//右子樹不為空
replaceInParent(node, right);//以右子樹根節點替換刪除節點
node.right = null;
} else {//兩個子樹均為空
replaceInParent(node, null);//以空節點替換節點,就是將該節點刪除掉
}
//那么從父節點開始,平衡被破壞,那么從父節點開始向根恢復平衡
rebalance(originalParent, false);
size--;
modCount++;
}
五、修改元素
Java
調用put方法,因為TreeMap中不能使用兩個相同的key。
也可以調用replace方法,來替換相同的元素,這個API是在1.8中增加的。
這個方法的原方法在Map中增加的。
@Override
public V replace(K key, V value) {
Entry<K,V> p = getEntry(key);//獲取一個節點,具體方法在四中有介紹
if (p!=null) {//如果有元素
V oldValue = p.value;//老值
p.value = value;//將值設為新值
return oldValue;//返回老值
}
return null;
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
Entry<K,V> p = getEntry(key);//找到節點
if (p!=null && Objects.equals(oldValue, p.value)) {//如果節點的值是原來的值
p.value = newValue;//替換它
return true;
}
return false;
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
int expectedModCount = modCount;
//循環將節點傳遞給function處理
//successor找到當前節點后一個節點
for (Entry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
e.value = function.apply(e.key, e.value);
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
//取得最小Key值的節點。。。
final Entry<K,V> getFirstEntry() {
Entry<K,V> p = root;
if (p != null)
while (p.left != null)
p = p.left;
return p;
}
Android
Android中沒有replace的API
六、查詢元素
Java
public V get(Object key) {
Entry<K,V> p = getEntry(key);//查詢節點
return (p==null ? null : p.value);//返回節點的值
}
Android
@Override public V get(Object key) {
Entry<K, V> entry = findByObject(key);//查詢節點
return entry != null ? entry.getValue() : null;//返回節點的值
}
七、包含元素
Java
實現了包含Key,實現是否包含值
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
public boolean containsValue(Object value) {
//從小到大遍歷元素節點,判斷是否有相等的值
for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
if (valEquals(value, e.value))
return true;
return false;
}
Android
實現了包含key
包含值采用的是父類的方法。
@Override public boolean containsKey(Object key) {
return findByObject(key) != null;//根據key找到的元素是否等于null
}
AbstractMap.java
public boolean containsValue(Object value) {
Iterator<Map.Entry<K, V>> it = entrySet().iterator();
if (value != null) {
while (it.hasNext()) {//使用迭代器查找相等的值
if (value.equals(it.next().getValue())) {
return true;
}
}
} else {
while (it.hasNext()) {//使用迭代器查找Null值
if (it.next().getValue() == null) {
return true;
}
}
}
return false;
}
8、其他常用方法
8.1 集合的大小
Java
public int size() {
return size;
}
Android
@Override public int size() {
return size;
}
8.2 集合是否為空
Java
AbstractMap.java
public boolean isEmpty() {
return size() == 0;
}
Android
@Override public boolean isEmpty() {
return size == 0;
}
8.3 清空
Java
將根節點指向空,數據等待GC
public void clear() {
modCount++;
size = 0;
root = null;
}
Android
將根節點指向空,數據等待GC
@Override public void clear() {
root = null;
size = 0;
modCount++;
}
其他文章
容器解析
ArrayList解析
LinkedList解析
TreeMap解析(上)
TreeMap解析(下)
HashMap解析
LinkedHasMap解析(下)
Set解析