Java TreeMap源码

141 阅读5分钟

TreeMap源码

TreeMap中的静态内部类Entry

  • 六个成员变量
    • key:键
    • value:值
    • left:左子节点
    • right:右子节点
    • parent:父节点
    • color:当前节点的颜色
private static final boolean RED = false;
private static final boolean BLACK = true;

static final class Entry<K, V> implements Map.Entry<K,V> {
    K key;
    V value;
    Entry<K, V> left;
    Entry<K, V> right;
    Entry<K, V> parent;
    boolean color = BLACK;
    
    Entry(K key, V value, Entry<K, V> parent) {
        this.key = key;
        this.value = value;
        this.parent = parent;
    }
    
    public K getKey()   {return key;}
    public V getValue() {return value;}
    
    public V setValue() {
        V oldValue = this.value;
        this.value = value;
        return oldValue;
    }
    
    public boolean equals(Object o) {
        return o instanceOf Map.Entry<?, ?> e
            && valEquals(key, e.getKey())
            && valEquals(value, e.getValue());
    }
    
    public int hashCode() {
        int keyHash = (key == null ? 0 : key.hashCode());
        int valueHash = (value == null ? 0 : value.hashCode());
        return keyHash ^ valueHash;
    }
    
    public String toString() {return key + "=" + value;}
    
}

TreeMap中的成员变量

  • Comparator:比较器,比较规则
private final Comparator<? super K> comparator;
  • root:根节点
private transient Entry<K, V> root;
  • size:结点个数
private transient int size = 0;
  • 操作次数
private transient int modCount = 0;

TreeMap中的构造方法

  • 空参构造
public TreeMap() {comparator = null;}
  • 带参构造
    • 传递了比较器对象
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

TreeMap中的put方法

  • 底层继续调用重载的put方法
public V put(K key, V value) {
    return put(key, value, true);
}

TreeMap中重载的put方法

  • 参数
    • key:键
    • value:值
    • replaceOld:当键重复时,是否覆盖值

方法流程

  • 创建结点t,将根节点root赋给t(给root赋一个新名字t)

  • 如果根节点t为空,说明TreeMap中没有任何元素
    • 调用addEntryToEmpthMap方法,给Map中添加父节点

  • 定义cmp用来获取比较结果

  • 创建结点parent

  • 创建比较器对象cpr来记录Map自身的比较器

  • 如果cpr比较器不为空,进入下面的do...while循环


  • 将t结点赋一个新名字parent(此时t和parent指向同一个结点)
  • 通过cpr比较器获取 parent结点记录的键 和 传进来的键 的比较结果赋值给cmp
    • 如果比较结果小于0
      • 给parent.left结点赋一个新名字t(此时parent就是t的父节点,t是parent的左子节点)
    • 如果比较结果大于0
      • 给parent.right结点赋一个新名字t(此时parent就是t的父节点,t是parent的右子节点)
    • 如果比较结果等于0
      • 新建变量oldValue记录parent结点的记录的值
        • 如果replaceOld为true(即键重复时,覆盖值)或oldValue为空
          • 将parent结点记录的的值更新
        • 返回oldValue

  • 判断t结点是否为空,如果为空,结束循环;如果不为空,继续上述循环
  • 如果cpr比较器为空(自然排序),执行下面的代码

  • 将要添加的键进行强转,强转为Comparable类型(所以当没有传递比较器对象时,需要实现Comparable接口)
  • 进入下面的do...while循环

  • 将t结点赋一个新名字parent(此时t和parent指向同一个结点)

  • 调用接口中重写的cmpareTo方法比较 parent结点记录的键 和 传进来的键 结果赋给cmp

    • 如果比较结果小于0
      • 给parent.left结点赋一个新名字t(此时parent就是t的父节点,t是parent的左子节点)
    • 如果比较结果大于0
      • 给parent.right结点赋一个新名字t(此时parent就是t的父节点,t是parent的右子节点)
    • 如果比较结果等于0
      • 新建变量oldValue记录parent结点的记录的值
        • 如果replaceOld为true(即键重复时,覆盖值)或oldValue为空
          • 将parent结点记录的的值更新
        • 返回oldValue

  • 判断t结点是否为空,如果为空,结束循环;如果不为空,继续上述循环

  • 直到这时可以判断需要在Map中添加新的键值对,调用addEntry方法,根据传递进去的 parent结点 和 cmp是否小于0 来添加新结点
    • 在添加完新的节点后,在addEntry方法中又会调用fixAfterInsertion方法根据红黑树规则对新添加的结点做一些调整
  • 添加的是新的结点,返回空null
private V put(K key, V value, boolean replaceOld) {
    Entry<K, V> t = root;
    if (t == null) {
        addEntryToEmpthMap(key, value);
        return null;
    }
    int cmp;
    Entry<K, V> parent;
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        do {
            parent = t;
            cmp = cpr.compare(key, t.ket);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else {
                V oldValue = t.value;
                if (replaceOld || oldValue == null) {
                    t.value = value;
                }
                return oldValue;
            }
        } while (t != null);
    } else {
        Object.requireNonNull(key);
        Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else {
                V oldValue = t.value;
                if (replaceOld || oldValue == null) {
                    t.value = value;
                }
                return oldValue;
            }
        } while (t != null);
    }
    addEntry(key, value, parent, cmp < 0);
    return null;
}

addEntryToEmpthMap方法

  • 建立新的根节点
private void addEntryToEmptyMap(K key, V value) {
    compare(key, key);  //自己跟自己比了一下,没有什么实际意义
    root = new Entry<>(key, value, null);
    size = 1;
    modCount++;
}

addEntry方法

  • 第四个参数传递进去的是 cmp < 0
    • 如果 cmp<0,为true,在左子节点添加新的结点
    • 如果 cmp>0,为false,在右子节点添加新的结点
  • 添加完后,调用fixAfterInsertion方法对新添加的结点做一些调整
private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
    if (addToLef)
        parent.left = e;
    else
        parent.right = e;
    fixAfterInsertion(e);
    size++;
    modCount++;
}

fixAfterInsertion方法

  • 按照红黑规则调整

Snipaste_2022-11-05_18-01-48.png

private void fixAfterInsertion(Entry<K, V> x) {
    x.color = RED;  //默认添加结点为红色
    
    while (x != null && x != root && x.parent.color == RED) {  //非根 父红色
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //判断父节点是祖父节点的左子节点还是右子节点
            Entry<K, V> y = rightOf(parentOf(parentOf(x))); //父节点是祖父节点的左子节点,获取祖父的右子节点为叔叔节点
            if (colorOf(y) == RED) {                     //叔叔红色
                setColor(parentOf(x), BLACK);            //将父设为黑色
                setColor(y, BLACK);                      //将叔叔设为黑色
                setColor(parentOf(parentOf(x)), RED);    //将祖父设为红色
                x = parentOf(parentOf(x));              //如果祖父非根,将祖父设置为当前节点再进行其他判断
            } else {                                  //叔叔黑色
                if (x == rightOf(parentOf(x))) {      //当前节点为父的右孩子 
                    x = parentOf(x);                  //把父作为当前节点
                    rotateLeft(x);                    //左旋,再进行其他判断
                } //下面没有用else包围是因为如果上面if语句执行了,得到的结果会自动满足下面的条件:当前节点为父的左孩子
                setColor(parentOf(x), BLACK);           //将父设为黑色
                setColor(parentOf(parentOf(x)), RED);   //将祖父设为红色
                rotateRight(parentOf(parentOf(x)));     //以祖父为支点进行右旋
            }
        } else {  //父节点是爷爷节点的右子节点,获取爷爷的左子节点为叔叔节点
            Entry<K, V> y = leftOf(parentOf(parentOf(x)));
            if (colorOf(y) == RED) {
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                x = parentOf(parentOf(x));
            } else {
                if (x == leftOf(parentOf(x))) {
                    x = parentOf(x);
                    rotateRight(x);
                }
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateLeft(parentOf(parentOf(x)));
            }
        }
    }
    root.color = BLACK;  //为根
}