JDK1.8 原始碼分析(十) -- TreeMap

我很醜發表於2020-03-06
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先

相關文章