getEntryUsingComparator
複製程式碼
1. 類的定義
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
複製程式碼
從類的定義中可以看出
- TreeMap是一個泛型類
- TreeMap繼承自AbstractMap
- TreeMap實現NavigableMap介面,表示TreeMap具有方向性,支援導航
- TreeMap實現Cloneable介面,表示TreeMap支援克隆
- TreeMap實現java.io.Serializable介面,表示TreeMap支援序列化
2.欄位屬性
//比較器,TreeMap的順序由比較器決定,如果比較器為空,順序由key自帶的比較器決定
private final Comparator<? super K> comparator;
//根節點,Entry是一個紅黑樹結構
private transient Entry<K,V> root;
//節點的數量
private transient int size = 0;
//修改統計
private transient int modCount = 0;
//快取鍵值對集合
private transient EntrySet entrySet;
//快取key的Set集合
private transient KeySet<K> navigableKeySet;
private transient NavigableMap<K,V> descendingMap;
複製程式碼
從欄位屬性中可以看出
- TreeMap是一個紅黑樹結構
- TreeMap儲存了根節點root
3.構造方法
//預設的構造方法
public TreeMap() {
//比較器預設為null
comparator = null;
}
//傳入一個比價器物件
public TreeMap(Comparator<? super K> comparator) {
//設定比較器
this.comparator = comparator;
}
//傳入一個Map物件
public TreeMap(Map<? extends K, ? extends V> m) {
//比較器設為null
comparator = null;
//新增Map物件的資料
putAll(m);
}
//傳入一個SortedMap物件
public TreeMap(SortedMap<K, ? extends V> m) {
//把SortedMap的比較器賦值給當前的比較器
comparator = m.comparator();
try {
//新增SortedMap物件的資料
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
複製程式碼
從構造方法中可以看出
- TreeMap預設比較器為null,實質上使用的是Key自帶的比較器,如果預設比較器為空,Key的物件必須實現Comparable介面
- TreeMap可以指定比較器進行初始化
- TreeMap可以接收Map物件來初始化
- TreeMap可以接收SortedMap物件來初始化
4.方法
size 方法
//獲取元素數量
public int size() {
//直接返回快取的size
return size;
}
複製程式碼
containsKey 方法
//是否存在key對應的節點
public boolean containsKey(Object key) {
//通過getEntry來獲取key對應的節點
return getEntry(key) != null;
}
複製程式碼
get 方法
//根據傳入的key查詢值
public V get(Object key) {
//通過getEntry來查詢對應key的節點
Entry<K,V> p = getEntry(key);
//如果節點不存在返回null,存在返回節點的value
return (p==null ? null : p.value);
}
複製程式碼
getEntry 方法
//根據傳入的key獲取對應的節點
final Entry<K,V> getEntry(Object key) {
if (comparator != null)
//如果存在比較器,呼叫getEntryUsingComparator方法來查詢節點
return getEntryUsingComparator(key);
//檢查key的合法性
if (key == null)
throw new NullPointerException();
//key的物件必須實現Comparable介面
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
//獲取根節點的副本
Entry<K,V> p = root;
//通過while迴圈查詢
while (p != null) {
//key進行比較
int cmp = k.compareTo(p.key);
if (cmp < 0)
//如果傳入的key比當前節點的key小,往左邊查詢
//把當前節點指向左子節點
p = p.left;
else if (cmp > 0)
//如果傳入的key比當前節點的key大,往右邊查詢
//把當前節點指向右子節點
p = p.right;
else
//查詢成功,返回當前節點
return p;
}
//沒找到,返回null
return null;
}
複製程式碼
getEntryUsingComparator 方法
//使用TreeMap自帶的比較器查詢節點
final Entry<K,V> getEntryUsingComparator(Object key) {
@SuppressWarnings("unchecked")
K k = (K) key;
//獲取比較器的副本
Comparator<? super K> cpr = comparator;
if (cpr != null) {
//如果比較器不為null
//獲取根節點的副本
Entry<K,V> p = root;
//使用while迴圈查詢
while (p != null) {
//使用TreeMap自帶的比較器進行比較
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
//如果傳入的key比當前節點的key小,往左邊查詢
//把當前節點指向左子節點
p = p.left;
else if (cmp > 0)
//如果傳入的key比當前節點的key大,往右邊查詢
//把當前節點指向右子節點
p = p.right;
else
//查詢成功,返回當前節點
return p;
}
}
//沒找到,返回null
return null;
}
複製程式碼
getCeilingEntry 方法
//返回僅僅大於等於傳入的key的節點
final Entry<K,V> getCeilingEntry(K key) {
//獲取根節點副本
Entry<K,V> p = root;
//while迴圈 p為null退出
while (p != null) {
//比較傳入的key和當前遍歷節點的key
int cmp = compare(key, p.key);
if (cmp < 0) {
//如果傳入的key小於當前遍歷節點的key
if (p.left != null)
//如果當前遍歷節點的左子節點不為null,往左繼續遍歷
//把當前遍歷節點指向左子節點
p = p.left;
else
//比當前節點小,但是當前節點不存在左子節點,返回當前節點
return p;
} else if (cmp > 0) {
//如果傳入的key大於當前遍歷節點的key
if (p.right != null) {
//如果當前遍歷節點右子節點不為null,往右繼續遍歷
//把當前遍歷節點指向右子節點
p = p.right;
} else {
//如果當前遍歷節點右子節點為null
//獲取當前遍歷節點的父節點
Entry<K,V> parent = p.parent;
//當前遍歷節點的副本
Entry<K,V> ch = p;
//while迴圈遍歷
//一直往上找,知道父節點是祖父節點的左子節點
while (parent != null && ch == parent.right) {
ch = parent;
parent = parent.parent;
}
//返回祖父節點
return parent;
}
} else
//傳入的key等於當前遍歷節點的key,返回當前節點
return p;
}
return null;
}
複製程式碼
getFloorEntry 方法
//返回僅僅小於等於傳入的key的節點
final Entry<K,V> getFloorEntry(K key) {
//獲取根節點的副本
Entry<K,V> p = root;
//while迴圈 p為null退出
while (p != null) {
//比較傳入的key和當前遍歷節點的key
int cmp = compare(key, p.key);
if (cmp > 0) {
//如果傳入的key大於當前遍歷節點的key
if (p.right != null)
//如果當前遍歷的key存在右子節點,往右繼續遍歷
//把當前遍歷節點指向右子節點
p = p.right;
else
//如果當前遍歷節點不存在右子節點,返回當前遍歷節點
return p;
} else if (cmp < 0) {
//如果傳入的key小於當前遍歷節點的key
if (p.left != null) {
//如果當前遍歷節點的左子節點不為null,往左繼續遍歷
//當前遍歷節點指向左子節點
p = p.left;
} else {
//如果當前遍歷節點左子節點為null
//獲取當前遍歷節點的父節點
Entry<K,V> parent = p.parent;
//當前遍歷節點的副本
Entry<K,V> ch = p;
//while迴圈遍歷
//一直往上找,直到父節點是祖父節點的右子節點
while (parent != null && ch == parent.left) {
ch = parent;
parent = parent.parent;
}
//返回祖父節點
return parent;
}
} else
//如果傳入的key等於當前遍歷節點,返回當前遍歷節點
return p;
}
return null;
}
複製程式碼
getHigherEntry 方法
//返回僅僅大於傳入的key的節點
final Entry<K,V> getHigherEntry(K key) {
//獲取根節點的副本
Entry<K,V> p = root;
//while迴圈,p為null退出
while (p != null) {
//比較傳入的key和當前遍歷節點key
int cmp = compare(key, p.key);
if (cmp < 0) {
//如果傳入的key小於當前遍歷節點的key
if (p.left != null)
//如果當前遍歷節點的左子節點不為null,繼續往左遍歷
//當前遍歷節點指向左子節點
p = p.left;
else
//如果當前遍歷節點的左子節點為null
//返回當前節點
return p;
} else {
//如果傳入的key大於或等於當前遍歷節點的key
if (p.right != null) {
//如果當前遍歷節點的右子節點不為null,往右繼續遍歷
//當前遍歷節點指向右子節點
p = p.right;
} else {
//如果當前遍歷節點的右子節點為null
//獲取當前遍歷節點的父節點
Entry<K,V> parent = p.parent;
//儲存當前遍歷節點的副本
Entry<K,V> ch = p;
//while迴圈遍歷
//一直往上找,直到父節點是祖父節點的左子子節點
while (parent != null && ch == parent.right) {
ch = parent;
parent = parent.parent;
}
//返回祖父節點
return parent;
}
}
}
return null;
}
複製程式碼
getLowerEntry
//返回僅僅小於傳入的key的節點
final Entry<K,V> getLowerEntry(K key) {
//獲取根節點的副本
Entry<K,V> p = root;
//while迴圈,p為null退出
while (p != null) {
//比較傳入的key和當前遍歷節點key
int cmp = compare(key, p.key);
if (cmp > 0) {
//如果傳入的key大於當前遍歷節點的key
if (p.right != null)
//如果當前遍歷的key存在右子節點,往右繼續遍歷
//把當前遍歷節點指向右子節點
p = p.right;
else
//如果當前遍歷節點不存在右子節點,返回當前遍歷節點
return p;
} else {
//如果傳入的key小於或等於當前遍歷節點的key
if (p.left != null) {
//如果當前遍歷節點的左子節點不為null,往左繼續遍歷
//當前遍歷節點指向左子節點
p = p.left;
} else {
//獲取當前遍歷節點的父節點
Entry<K,V> parent = p.parent;
//當前遍歷節點的副本
Entry<K,V> ch = p;
//while迴圈遍歷
//一直往上找,直到父節點是祖父節點的右子節點
while (parent != null && ch == parent.left) {
ch = parent;
parent = parent.parent;
}
//返回祖父節點
return parent;
}
}
}
return null;
}
複製程式碼
containsValue 方法
//是否存在對應的值
public boolean containsValue(Object value) {
//使用for迴圈查詢
//getFirstEntry 獲取最左子節點,即最小的節點
//successor 獲取當前節點的僅比當前節點大的下一個節點
//for迴圈是根據key從小到大遍歷
for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
//使用valEquals 比對值
if (valEquals(value, e.value))
//如果存在,返回true
return true;
//不存在,返回false
return false;
}
複製程式碼
getFirstEntry 方法
//獲取第一個節點,也是key最小的節點,即最左子節點
final Entry<K,V> getFirstEntry() {
//獲取根節點的副本
Entry<K,V> p = root;
if (p != null)
//如果根節點不為null
//從根節點開始遍歷整棵樹
while (p.left != null)
//把節點指向左子節點
p = p.left;
//返回一直往左找到的節點
return p;
}
複製程式碼
firstEntry 方法
//深拷貝第一個節點
public Map.Entry<K,V> firstEntry() {
//getFirstEntry 獲取第一個節點,也是key最小的節點,即最左子節點
//exportEntry 返回一個新的SimpleImmutableEntry物件
return exportEntry(getFirstEntry());
}
複製程式碼
getLastEntry 方法
//獲取最右一個節點,也是key最大的節點,即最右子節點
final Entry<K,V> getLastEntry() {
//獲取根節點的副本
Entry<K,V> p = root;
if (p != null)
//如果根節點不為null
//從根節點開始遍歷整棵樹
while (p.right != null)
//把節點指向右子節點
p = p.right;
//返回一直往右找到的節點
return p;
}
複製程式碼
lastEntry 方法
//深拷貝最後一個節點
public Map.Entry<K,V> lastEntry() {
//getLastEntry 獲取最右一個節點,也是key最大的節點,即最右子節點
//exportEntry 返回一個新的SimpleImmutableEntry物件
return exportEntry(getLastEntry());
}
複製程式碼
successor 方法
//查詢key僅比當前節點大的節點
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
//傳入節點為null,直接返回null
return null;
//先找右子節點的最左子節點
else if (t.right != null) {
//如果右子節點不為null
//儲存右子節點的副本
Entry<K,V> p = t.right;
//使用while迴圈一直往左找
while (p.left != null)
p = p.left;
//返回最後找到的節點
return p;
} else {
//如果當前節點是右節點
//while迴圈一直往上找,直到找到父類是左子節點,左邊的始終小於右邊
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
複製程式碼
valEquals 方法
//比較兩個物件是否相等
static final boolean valEquals(Object o1, Object o2) {
return (o1==null ? o2==null : o1.equals(o2));
}
複製程式碼
comparator 方法
//獲取比較器
public Comparator<? super K> comparator() {
return comparator;
}
複製程式碼
firstKey 方法
//獲取第一個節點(最左子節點)的key
public K firstKey() {
//getFirstEntry 獲取第一個節點
//key 獲取節點的key
return key(getFirstEntry());
}
複製程式碼
lastKey 方法
//獲取最後一個節點(最右子節點)的key
public K lastKey() {
//getLastEntry 獲取最後一個節點
//key 獲取節點的key
return key(getLastEntry());
}
複製程式碼
key 方法
//獲取節點的key
static <K> K key(Entry<K,?> e) {
//節點為null,丟擲異常
if (e==null)
throw new NoSuchElementException();
return e.key;
}
複製程式碼
keyOrNull 方法
//獲取節點的key,節點為null返回null
static <K,V> K keyOrNull(TreeMap.Entry<K,V> e) {
return (e == null) ? null : e.key;
}
複製程式碼
pollFirstEntry 方法
//獲取第一個元素並刪除
public Map.Entry<K,V> pollFirstEntry() {
//getFirstEntry 獲取第一個節點,也是key最小的節點,即最左子節點
Entry<K,V> p = getFirstEntry();
//根據p生成一個新的Entry物件
Map.Entry<K,V> result = exportEntry(p);
if (p != null)
//如果p不為null,刪除p
deleteEntry(p);
//返回新生成的物件
return result;
}
複製程式碼
pollLastEntry 方法
//獲取最後一個元素並刪除
public Map.Entry<K,V> pollLastEntry() {
//getLastEntry 獲取最右一個節點,也是key最大的節點,即最右子節點
Entry<K,V> p = getLastEntry();
//根據p生成一個新的Entry物件
Map.Entry<K,V> result = exportEntry(p);
if (p != null)
//如果p不為null,刪除p
deleteEntry(p);
//返回新生成的物件
return result;
}
複製程式碼
lowerEntry 方法
//獲取僅僅小於傳入的key的節點,幷包裝成一個新的物件
public Map.Entry<K,V> lowerEntry(K key) {
//getLowerEntry 獲取僅僅小於傳入的key的節點
//exportEntry 包裝成一個新的物件
return exportEntry(getLowerEntry(key));
}
複製程式碼
lowerKey 方法
//獲取僅僅小於傳入的key的節點的key
public K lowerKey(K key) {
//getLowerEntry 獲取僅僅小於傳入的key的節點
//keyOrNull 獲取節點的key
return keyOrNull(getLowerEntry(key));
}
複製程式碼
floorEntry 方法
//返回僅僅小於等於傳入的key的節點,幷包裝成一個新的物件
public Map.Entry<K,V> floorEntry(K key) {
//getFloorEntry 返回僅僅小於等於傳入的key的節點
//exportEntry 包裝成一個新的物件
return exportEntry(getFloorEntry(key));
}
複製程式碼
floorKey 方法
//返回僅僅小於等於傳入的key的節點的key
public K floorKey(K key) {
//getFloorEntry 返回僅僅小於等於傳入的key的節點
//keyOrNull 獲取節點的key
return keyOrNull(getFloorEntry(key));
}
複製程式碼
ceilingEntry 方法
//返回僅僅大於等於傳入的key的節點, 幷包裝成一個新的物件
public Map.Entry<K,V> ceilingEntry(K key) {
//getCeilingEntry 返回僅僅大於等於傳入的key的節點
//exportEntry 包裝成一個新的物件
return exportEntry(getCeilingEntry(key));
}
複製程式碼
ceilingKey 方法
//返回僅僅大於等於傳入的key的節點的key
public K ceilingKey(K key) {
//getCeilingEntry 返回僅僅大於等於傳入的key的節點
//keyOrNull 獲取節點的key
return keyOrNull(getCeilingEntry(key));
}
複製程式碼
higherEntry 方法
//返回僅僅大於傳入的key的節點, 幷包裝成一個新的物件
public Map.Entry<K,V> higherEntry(K key) {
//getHigherEntry 返回僅僅大於傳入的key的節點
//exportEntry 包裝成一個新的物件
return exportEntry(getHigherEntry(key));
}
複製程式碼
higherKey 方法
//返回僅僅大於傳入的key的節點的key
public K higherKey(K key) {
//getHigherEntry 返回僅僅大於傳入的key的節點
//keyOrNull 獲取節點的key
return keyOrNull(getHigherEntry(key));
}
複製程式碼
keySet 方法
//獲取所有key的Set集合
public Set<K> keySet() {
//呼叫navigableKeySet 方法獲取
return navigableKeySet();
}
複製程式碼
navigableKeySet 方法
//返回key的Set集合
public NavigableSet<K> navigableKeySet() {
//先使用快取
KeySet<K> nks = navigableKeySet;
//如果快取為空,重新建立KeySet物件
//注意,KeySet是TreeMap的內部類,可以獲取TreeMap物件所有的屬性
//KeySet是NavigableSet的子類,KeySet實現了NavigableSet介面
return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
}
複製程式碼
descendingKeySet 方法
//獲取descendingMap
public NavigableSet<K> descendingKeySet() {
return descendingMap().navigableKeySet();
}
複製程式碼
descendingMap 方法
//返回descendingMap
public NavigableMap<K, V> descendingMap() {
NavigableMap<K, V> km = descendingMap;
//如果快取為空,重新建立DescendingSubMap物件
//注意,DescendingSubMap是TreeMap的內部類,可以獲取TreeMap物件所有的屬性
return (km != null) ? km :
(descendingMap = new DescendingSubMap<>(this,
true, null, true,
true, null, true));
}
複製程式碼
values 方法
//獲取所有value的Colletion集合
public Collection<V> values() {
//使用快取
Collection<V> vs = values;
if (vs == null) {
//如果快取為空,重新建立Values物件
//注意,Values是TreeMap的內部類,可以獲取TreeMap物件所有的屬性
vs = new Values();
values = vs;
}
//返回value的Colletion集合
return vs;
}
複製程式碼
entrySet 方法
//獲取所有鍵值對的Entry物件
public Set<Map.Entry<K,V>> entrySet() {
//使用快取
EntrySet es = entrySet;
//如果快取為空,重新建立EntrySet物件
//注意,EntrySet是TreeMap的內部類,可以獲取TreeMap物件所有的屬性
return (es != null) ? es : (entrySet = new EntrySet());
}
複製程式碼
replace 方法
@Override
//修改節點的值,需要比對舊值
public boolean replace(K key, V oldValue, V newValue) {
//通過getEntry 獲取節點
Entry<K,V> p = getEntry(key);
if (p!=null && Objects.equals(oldValue, p.value)) {
//如果節點不為null,並且節點的值跟傳入的oldValue相等
//把新值賦值給節點
p.value = newValue;
//修改成功,返回true
return true;
}
//節點不存在,返回false
return false;
}
@Override
//修改節點的值,不需要比對舊值
public V replace(K key, V value) {
//通過getEntry 獲取節點
Entry<K,V> p = getEntry(key);
if (p!=null) {
//如果節點不為null
//獲取節點的舊值
V oldValue = p.value;
//把新值賦值給節點
p.value = value;
//返回舊值
return oldValue;
}
//節點不存在,返回null
return null;
}
複製程式碼
forEach 方法
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
//檢查引數的合法性
Objects.requireNonNull(action);
//獲取修改統計的副本
int expectedModCount = modCount;
//遍歷節點,每個節點都呼叫BiConsumer方法
for (Entry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
action.accept(e.key, e.value);
//如果遍歷過程中,有其他執行緒修改,丟擲異常
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
複製程式碼
replaceAll 方法
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
//檢查引數的合法性
Objects.requireNonNull(function);
//獲取修改統計的副本
int expectedModCount = modCount;
//遍歷節點,每個節點都呼叫BiFunction方法
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();
}
}
}
複製程式碼
exportEntry 方法
//把Entry物件包裝成一個新的物件,深拷貝?
static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
return (e == null) ? null :
new AbstractMap.SimpleImmutableEntry<>(e);
}
複製程式碼
putAll 方法
//新增Map物件的元素
public void putAll(Map<? extends K, ? extends V> map) {
//獲取傳入map物件的大小
int mapSize = map.size();
if (size==0 && mapSize!=0 && map instanceof SortedMap) {
//如果map為SortedMap
//獲取map的比較器
Comparator<?> c = ((SortedMap<?,?>)map).comparator();
if (c == comparator || (c != null && c.equals(comparator))) {
//如果map的比較器和當前的比較器相等
//把修改統計加1
++modCount;
try {
//使用buildFromSorted新增元素
buildFromSorted(mapSize, map.entrySet().iterator(),
null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
return;
}
}
//如果只是普通的map物件,使用父類的方法新增元素
super.putAll(map);
}
//父類的putAll方法
public void putAll(Map<? extends K, ? extends V> m) {
//迴圈遍歷Map元素,獲取鍵值對,呼叫put方法新增
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
//呼叫put方法新增元素
put(e.getKey(), e.getValue());
}
複製程式碼
put 方法
//新增鍵值對元素
public V put(K key, V value) {
//獲取根節點副本
Entry<K,V> t = root;
if (t == null) {
//如果根節點為null
//型別檢查和空檢查,如果當前比較器為null,key的物件必須實現Comparable介面
compare(key, key); // type (and possibly null) check
//根據key和value建立新的節點,並把建立的新節點作為根節點
root = new Entry<>(key, value, null);
//把元素數量置為1
size = 1;
//修改統計加1
modCount++;
//返回null
return null;
}
//根節點不為null的情況
int cmp;
//parent 插入節點的父節點
Entry<K,V> parent;
// split comparator and comparable paths
//獲取當前的比較器
Comparator<? super K> cpr = comparator;
if (cpr != null) {
//如果存在比較器
//while迴圈遍歷查詢
do {
//把parent指向當前節點
parent = t;
//表當前節點的key和傳入的key
cmp = cpr.compare(key, t.key);
if (cmp < 0)
//如果傳入的key小於當前節點的key,往左查詢
//把當前節點指向左子節點
t = t.left;
else if (cmp > 0)
//如果傳入的key大於當前節點的key,往右查詢
//把當前節點指向右子節點
t = t.right;
else
//如果查詢到了,把當前節點的值替換為傳入的值
return t.setValue(value);
} while (t != null);
}
//不存在比較器,使用key的比較器
else {
if (key == null)
//如果傳入key為空,丟擲異常
throw new NullPointerException();
//強轉key
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
//while迴圈遍歷查詢
do {
//把parent指向當前節點
parent = t;
//使用key自帶的比較器進行比較
cmp = k.compareTo(t.key);
if (cmp < 0)
//如果傳入的key小於當前節點的key,往左查詢
//把當前節點指向左子節點
t = t.left;
else if (cmp > 0)
//如果傳入的key大於當前節點的key,往右查詢
//把當前節點指向右子節點
t = t.right;
else
//如果查詢到了,把當前節點的值替換為傳入的值
return t.setValue(value);
} while (t != null);
}
//把最後遍歷的節點作為父節點,建立新的節點
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
//如果插入的key比parent的key小,新的節點作為左子節點
parent.left = e;
else
//如果插入的key比parent的key大,新的節點作為右子節點
parent.right = e;
//插入過後進行平衡操作
fixAfterInsertion(e);
//元素數量加1
size++;
//修改統計加1
modCount++;
//返回null
return null;
}
複製程式碼
fixAfterInsertion 方法
//插入節點後,平衡紅黑樹
private void fixAfterInsertion(Entry<K,V> x) {
//把插入的節點預設為紅節點
x.color = RED;
//while迴圈操作
//如果當前節點不為空,當前節點不是根節點,當前節點的父節點是紅節點就一直迴圈
//直到父節點是黑節點或者達到根節點,才退出迴圈
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)));
//進行下一次迴圈
}
//*************************end************************
} 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)));
//進行下一次迴圈
}
//============================end=====================
}
}
//把根節點染色為黑節點
root.color = BLACK;
}
複製程式碼
從上面可以看出 染色=>左旋=>右旋 或者是 染色=>右旋=>左旋
-
插入的節點預設為紅節點,這樣改動最小
-
如果叔叔節點是紅節點
- 把父節點和叔叔節點染色為黑節點,祖父節點染色為紅節點。再以祖父節點為插入的新節點重新進行操作
-
如果叔叔節點是黑節點
-
如果當前節點和叔叔節點同為左子節點
- 把當前節點指向父節點
- 以當前節點為基礎節點進行右旋
- 把父節點染色為黑節點
- 把祖父節點染色為紅節點
- 以祖父節點為基礎幾點進行左旋
- 然後再進行下一次迴圈
-
如果當前節點為右子節點,叔叔節點為左子節點
- 把父節點染色為黑節點
- 把祖父節點染色為紅節點
- 以祖父節點為基礎幾點進行左旋
- 然後再進行下一次迴圈
-
如果當前節點和叔叔節點同為右子節點
- 把當前節點指向父節點
- 以當前節點為基礎節點進行左旋
- 把父節點染色為黑節點
- 把祖父節點染色為紅節點
- 以祖父節點為基礎幾點進行左旋
- 然後再進行下一迴圈
-
如果當前節點為左子子節點,叔叔節點為右子節點
- 把父節點染色為黑節點
- 把祖父節點染色為紅節點
- 以祖父節點為基礎幾點進行左旋
- 然後再進行下一迴圈
-
rotateLeft 方法
//左旋
private void rotateLeft(Entry<K,V> p) {
if (p != null) {
//如果當前節點不為null
//獲取當前節點的右子節點
Entry<K,V> r = p.right;
//把當前節點的右子節點指向當前節點的右子節點的左子節點
p.right = r.left;
if (r.left != null)
//如果當前節點的右子節點的左子節點不為null
//把當前節點的右子節點的左子節點的父節點指向當前節點
r.left.parent = p;
//當前節點的右子節點的父節點指向當前節點的父節點
r.parent = p.parent;
if (p.parent == null)
//如果當前節點的父節點為null,表示當前節點為根節點
//把根節點指向當前節點的右子節點
root = r;
else if (p.parent.left == p)
//如果當前節點是父節點的左子節點
//把當前節點父節點的左子節點指向當前節點的右子節點
p.parent.left = r;
else
//如果當前節點是父節點的右子節點
//把當前節點父節點的右子節點指向當前節點的右子節點
p.parent.right = r;
//當前節點的右節點的左子節點指向當前節點
r.left = p;
//當前節點的父節點指向右子節點
p.parent = r;
}
}
複製程式碼
從上面可以看出,左旋的本質是
- 當前節點p成為當前節點p的右子節點pr的左子節點
- 當前節點p的右子節點變成右子節點pr的左子節點prl
rotateRight 方法
//右旋
private void rotateRight(Entry<K,V> p) {
if (p != null) {
//如果當前節點不為null
//獲取當前節點左子節點
Entry<K,V> l = p.left;
//當前節點的左子節點指向當前節點左子節點的右子節點
p.left = l.right;
//如果當前節點的左子節點的右子節點不為null
//把當前節點的左子節點的右子節點的父節點指向當前節點
if (l.right != null) l.right.parent = p;
//當前節點的左子節點的父節點指向當前節點的父節點
l.parent = p.parent;
if (p.parent == null)
//如果當前節點的父節點為null,表示當前節點是根節點
//把根節點指向當前節點的左子節點
root = l;
else if (p.parent.right == p)
//如果當前節點是父節點的右子節點
//把當前節點的父節點的右子節點指向當前節點的左子節點
p.parent.right = l;
//如果當前節點是父節點的左子節點
//把當前節點的父節點的左子節點指向當前節點的左子節點
else p.parent.left = l;
//當前節點的左子節點的右節點指向當前節點
l.right = p;
//當前節點的父節點指向當前節點的左子節點
p.parent = l;
}
}
複製程式碼
從上面可以看出,右旋的本質是
- 當前節點p成為當前節點p的左子節點pl的右子節點
- 當前節點p的左子節點變成左子節點pl的右子節點plr
remove 方法
//移除對應key的節點
public V remove(Object key) {
//使用getEntry獲取key對應的節點
Entry<K,V> p = getEntry(key);
if (p == null)
//如果不存在對應的節點,返回null
return null;
//獲取對應節點的值
V oldValue = p.value;
//使用deleteEntry刪除節點
deleteEntry(p);
//返回舊值
return oldValue;
}
複製程式碼
clear 方法
//清除所有元素
public void clear() {
//修改統計加1
modCount++;
//元素數量置為0
size = 0;
//根節點置為null
root = null;
}
複製程式碼
deleteEntry 方法
//刪除節點
private void deleteEntry(Entry<K,V> p) {
//修改統計加1
modCount++;
//元素數量減1
size--;
// If strictly internal, copy successor's element to p and then make p
// point to successor.
if (p.left != null && p.right != null) {
//如果p節點的左右子節點都存在
//查詢僅僅比p大的元素
Entry<K,V> s = successor(p);
//把p的key和value設定為s的key和value
p.key = s.key;
p.value = s.value;
//把p指向s
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) {
//如果p存在子節點
// Link replacement to parent
//***************replacement連線到p的父節點********************
replacement.parent = p.parent;
if (p.parent == null)
//如果p的父節點為null,表示p節點為根節點
//把根節點置為replacement
root = replacement;
else if (p == p.parent.left)
//如果p節點是父節點的左子節點
//把p節點的父節點的左子節點指向replacement
p.parent.left = replacement;
else
//如果p節點是父節點的右子節點
//把p節點的父節點的右子節點指向replacement
p.parent.right = replacement;
//***************end********************
//把p節點對外的引用都置為null
p.left = p.right = p.parent = null;
// Fix replacement
if (p.color == BLACK)
//如果p節點是黑節點
//呼叫fixAfterDeletion使紅黑樹達到平衡
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
//如果p的父節點為null,表示p節點為根節點
//把root置為null即可
root = null;
} else { // No children. Use self as phantom replacement and unlink.
//p不存在子節點的情況下
if (p.color == BLACK)
//如果p節點是黑節點
//呼叫fixAfterDeletion使紅黑樹達到平衡
fixAfterDeletion(p);
if (p.parent != null) {
//如果p的父節點不為null
if (p == p.parent.left)
//如果p是父節點的左子節點
//把父節點的左子節點置為null
p.parent.left = null;
else if (p == p.parent.right)
//如果p是父節點的右子節點
//把父節點的右子節點置為null
p.parent.right = null;
//把p的父節點引用置為null
p.parent = null;
}
}
}
複製程式碼
fixAfterDeletion 方法
//刪除節點後,平衡紅黑樹
private void fixAfterDeletion(Entry<K,V> x) {
//如果x不是根節點並且x是黑節點一直進行while迴圈
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
//如果當前節點是父節點的左子節點
//獲取當前節點的父節點的右子節點,即右兄弟節點
Entry<K,V> sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
//如果右兄弟節點是紅節點
//把右兄弟節點染色成為黑節點
setColor(sib, BLACK);
//把當前節點的父節點染色稱為紅節點
setColor(parentOf(x), RED);
//以父節點為基礎節點進行左旋
rotateLeft(parentOf(x));
//重新設定右兄弟節點
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
//如果右兄弟節點的子節點都是黑節點
//把右兄弟節點染色成為紅節點
setColor(sib, RED);
//把當前節點指向父節點
x = parentOf(x);
//進行下一次迴圈
} else {
//右兄弟節點的子節點有紅節點的情況
if (colorOf(rightOf(sib)) == BLACK) {
//如果右兄弟節點的右子節點為黑節點(左子節點為紅節點)
//把右兄弟節點的左子節點染色成為黑節點
setColor(leftOf(sib), BLACK);
//把右兄弟節點染色成為紅節點
setColor(sib, RED);
//以右兄弟節點為基礎節點,進行右旋
rotateRight(sib);
//重新設定右兄弟節點
sib = rightOf(parentOf(x));
}
//把右兄弟節點的顏色染色成為父節點的顏色
setColor(sib, colorOf(parentOf(x)));
//把父節點染色成為黑節點
setColor(parentOf(x), BLACK);
//把右兄弟節點的右子節點染色成為黑節點
setColor(rightOf(sib), BLACK);
//以父節點為基礎節點進行左旋
rotateLeft(parentOf(x));
//把指向root節點,退出while迴圈
x = root;
}
} 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));
//把當前節點指向根節點,退出while迴圈
x = root;
}
}
}
//把當前節點x染色成為黑節點
setColor(x, BLACK);
}
複製程式碼
小結:
- 對紅黑樹的刪除後的平衡操作還不是很理解
- 染色,左旋,右旋是平衡的關鍵
- 後面資料結構章節再結合圖來進行說明,立個flag先