JDK1.8 源码分析(十) -- TreeMap

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;
    }
复制代码

从上面可以看出 染色=>左旋=>右旋 或者是 染色=>右旋=>左旋

  • 插入的节点默认为红节点,这样改动最小

  • 如果叔叔节点是红节点

    • 把父节点和叔叔节点染色为黑节点,祖父节点染色为红节点。再以祖父节点为插入的新节点重新进行操作
  • 如果叔叔节点是黑节点

    • 如果当前节点和叔叔节点同为左子节点

      • 把当前节点指向父节点
      • 以当前节点为基础节点进行右旋
      • 把父节点染色为黑节点
      • 把祖父节点染色为红节点
      • 以祖父节点为基础几点进行左旋
      • 然后再进行下一次循环
    • 如果当前节点为右子节点,叔叔节点为左子节点

      • 把父节点染色为黑节点
      • 把祖父节点染色为红节点
      • 以祖父节点为基础几点进行左旋
      • 然后再进行下一次循环
    • 如果当前节点和叔叔节点同为右子节点

      • 把当前节点指向父节点
      • 以当前节点为基础节点进行左旋
      • 把父节点染色为黑节点
      • 把祖父节点染色为红节点
      • 以祖父节点为基础几点进行左旋
      • 然后再进行下一循环
    • 如果当前节点为左子子节点,叔叔节点为右子节点

      扫描二维码关注公众号,回复: 9645775 查看本文章
      • 把父节点染色为黑节点
      • 把祖父节点染色为红节点
      • 以祖父节点为基础几点进行左旋
      • 然后再进行下一循环

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先

猜你喜欢

转载自juejin.im/post/5e621592f265da573d61b1fb
今日推荐