TreeMap源码解析


如果要理解TreeMap的源码,那就先要理解红黑树的各种操作

红黑树

  • 定义

    1. 红黑树实质上就是一颗自平衡的二叉查找树,有时候形态不一定是平衡二叉树,满足以下的条件(左子树节点值<根节点值<右子树节点值)
    2. 引入带颜色的节点也是为了方便在进行插入或删除时,如果破坏了二叉树的平衡性能通过一系列变换保持平衡
    3. 红黑树的查找、插入、删除的时间复杂度均为Olog2n
    4. 适用于频繁插入、删除的场景,实用性更强。
  • 性质

    1. 节点是红的或黑的
    2. 根节点是黑色
    3. 叶子节点(也叫外部节点、NULL节点、失败节点)是黑色
    4. 不存在两个相邻的红节点
    5. 从任意节点到可达的叶子节点的每个路径包含相同数目的黑色节点
  • 口诀

    1. 大小左根右,根叶结点黑(性质2+3),父子不红红(性质4),路路同黑数(性质5)。
  • 红黑树的插入

  • 先查找,确定插入位置(原理同二叉排序树),插入新节点

  • 新节点是根----染成黑色

  • 新节点是非根----染成红色

    • 若插入新节点后依然满足红黑树定义,则插入结束
    • 若插入新节点后不满足红黑树定义需要调整,使其重新满足红黑树的定义
      • 黑叔:旋转+染色
        • LL型:右单旋,父换爷+染色
        • RR型:左单旋,父换爷+染色
        • LR型:左、右双旋 父换爷+染色
        • RL型:右、左双旋,儿换爷+染色
      • 红叔:染色+变新
        • 叔父爷染色,爷变为新节点

转载一篇关于红黑树的一篇文章,非常的浅显易懂

红黑树插入详解


开始我们今天的正题了

TreeMap的源码解析

首先你要明白以下的属性

这个属性是TreeMap集合实现能进行排序的关键

private final Comparator<? super K> comparator;

TreeMap存储的节点

private transient Entry<K,V> root;

TreeMap的长度

private transient int size = 0;

TreeMap的修改次数

private transient int modCount = 0;

//静态内部类,定义了每个节点的详细信息

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;

}

Java中要使用一个对象,那就必须创建一个对象,创建一个对象可以使用工厂模式创建,而最为常见的创建对象的方法是通过new

所有来看TreeMap的的构造器

//无参构造器
public TreeMap() {
    comparator = null;
}
//这是重新比较器后把比较器复制给TreeMap对象
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}
//调用了putAll(m);
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}

创建对象后,就是添加数据的方法了,put方法

public V put(K key, V value) {
    	//创建一个根节点
        Entry<K,V> t = root;
    	
        if (t == null) {
            //调用比较方法
            compare(key, key); // type (and possibly null) check
            //创建头结点
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
    	//这里开始判断你使用的是泛型中的比较器,还是TreeMap中比较器
        // split comparator and comparable paths
    	//TreeMap比较器的优先级更加的高
        Comparator<? super K> cpr = comparator;
   		//得到Parent节点的地址
        if (cpr != null) {
            //把根节点与放入的节点进行比较
            do {
                //主要的作用是获取插入节点父节点的位置
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    //相同的话,就返回oldValue
                    return t.setValue(value);
            } while (t != null);
        }
    	//这是相同的道理,就是比较器对象不同而已
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                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
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
    	//得到之前的cmp然后把该节点放在对应的位置
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
    	//这个方法就是插入后,调整位置,保持红黑树的方法
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

compare

//比较大小,判断次节点在左节点还是在右节点
@SuppressWarnings("unchecked")
    final int compare(Object k1, Object k2) {
        //判断是否重写了比较方法,如果没有重写比较方法的话,就去父类中寻找比较方法
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            //使用重写的比较方法
            : comparator.compare((K)k1, (K)k2);
    }

fixAfterInsertion

//得到该节点的颜色

private static <K,V> boolean colorOf(Entry<K,V> p) {
return (p == null ? BLACK : p.color);
}

得到该节点的父节点

private static <K,V> Entry<K,V> parentOf(Entry<K,V> p) {
return (p == null ? null: p.parent);
}

给节点设置颜色的方法

private static <K,V> void setColor(Entry<K,V> p, boolean c) {
if (p != null)
p.color = c;
}

得到该节点的左节点

private static <K,V> Entry<K,V> leftOf(Entry<K,V> p) {
return (p == null) ? null: p.left;
}

得到该节点的右节点

private static <K,V> Entry<K,V> rightOf(Entry<K,V> p) {
return (p == null) ? null: p.right;
}


怎么重新调整为红黑树可以参考红黑树的插入

private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个不爱学习的小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值