一起来看源代码-01TreeMap添加操作

本文作者:黄海燕,叩丁狼高级讲师。原创文章,转载请注明出处。

##前言

之前很多小伙伴问我怎么看源代码,还有就是越来越多的程序员都想要看源代码,搞懂底层原理,但是感觉源代码非常的晦涩难懂,不够直接和清晰,所以我希望这篇文章能够快速带同学们看懂java源码,更加深入的学习java,帮助小伙伴们节约学习的时间成本.

##1.树的介绍

  • 什么是树结构?其实就是一个节点下面有多个子节点,我们称之为树结构,如下图:
  • 普通节点:拥有子节点的节点。
  • 叶子节点:没有子节点的节点。

一起来看源代码-01TreeMap添加操作_第1张图片

什么是二叉树?

  • 二叉树就是一个节点最多只能有2个子节点,分为左节点和右节点,如下图:
    一起来看源代码-01TreeMap添加操作_第2张图片

什么是排序二叉树?

  • 若左子树不为空,则左子树所有节点的值小于根节点的值。
  • 若右子树不为空,则右子树所有节点的值大于根节点的值。
    如图:
    一起来看源代码-01TreeMap添加操作_第3张图片

什么是红黑树?
红黑树其实是一个平衡排序二叉树,属于查询高效的树结构.请看下图:
一起来看源代码-01TreeMap添加操作_第4张图片

查询元素6普通的二叉树需要操作6次,红黑树只需要操作4次,所以红黑树查询更加高效.

##1.TreeMap的结构介绍
###1.1 结构关系

public class TreeMap
    extends AbstractMap
    implements NavigableMap, Cloneable, java.io.Serializable{

继承关系:

  • 父类AbstractMap,让子类拥有基本Map的方法,比如增(put)删(remove)查(get)等方法.

实现关系:

  • NavigableMap:父接口为SortedMap,所以NavigableMap为可排序接口,表示TreeMap但是一个排序的Map:
  • Cloneable:标记型的接口,内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆,能使用Object.clone()方法。如果没有实现 Cloneable的类对象调用clone()就会抛出CloneNotSupportedException。
  • Serializable:标记型的接口,内部都没有方法和属性,实现Serializable表示可进行序列化和反序列化,表示能够使用在ObjectOutputStream.writeObject())和ObjectInputStream.readObject()

###1.2 基本成员组成

   /**
     * 比较器:类似于一个"裁判",用于比较插入对象和树节点的内容大小
     * 因为TreeMap是一个红黑树,红黑树是一个排序二叉树,插入元素的时候需要进行排序,排序可以使用比较器中的方式排序
     */
    private final Comparator comparator;
    /**
     *  根节点
     */
    private transient Entry root;
    /**
     * 树的元素个数
     */
    private transient int size = 0;
    /**
     * 操作次数:增加和删除操作数都会加1,用于控制并发修改异常
     */
    private transient int modCount = 0;

通过root根节点可以看出一个节点(元素)就是一个Entry对象,所以一个节点(元素)中包含多个数据.结构如下:

    static final class Entry implements Map.Entry {
        K key;//键
        V value;//值
        Entry left;//左节点
        Entry right;//右节点
        Entry parent;//父节点
        boolean color = BLACK;//颜色

节点表示如下:
一起来看源代码-01TreeMap添加操作_第5张图片

###1.3添加操作:

public V put(K key, V value) {
        Entry t = root;//和插入节点进行比较的树节点
        //根节点为空
        if (t == null) {
            compare(key, key); //key比key,自己比较自己,目的是想要检查类型,确保传入了比较器或者是key实现了可比较接口
            //创建了一个没有父节点的新的节点,作为根节点
            root = new Entry<>(key, value, null);
            size = 1;//元素个数为1
            modCount++;//操作数+1
            return null;//返回空,根节点添加结束
        }
        int cmp;//表示比较结果
        Entry parent;
        // cpr临时表示比较器
        Comparator cpr = comparator;
        if (cpr != null) {//比较器不为空,就使用比较器比较元素
            do {
                parent = t;//父节点为t
                cmp = cpr.compare(key, t.key);//通过比较器对key和树节点比较得到比较结果
                if (cmp < 0)//比较结果小于0,表示插入的元素比树节点小
                    t = t.left;//t往左走
                else if (cmp > 0)//比较结果大于0,表示插入的元素比树节点大
                    t = t.right;//t往右走
                else//cmp比较结果为0,覆盖节点中的value
                    return t.setValue(value);
            } while (t != null);//直到t为空停下来,保证插入的是节点
        } else {//比较器为空就使用元素中的比较方法
            if (key == null)//插入的元素key为空,没办法和树结构中的元素比较,抛出空指针异常
                throw new NullPointerException();
            /*
             *  key进行强转,看一下key是否实现了Comparable接口,是否存在compareTo方法,
             *  如果没有实现接口,也就不确定有没有compareTo方法了,没办法进行比较了
             */
            Comparable k = (Comparable) key;
            do {
                parent = t;//父节点为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 e = new Entry<>(key, value, parent);
        if (cmp < 0)//如果比较结果小于0插入左边
            parent.left = e;
        else
            parent.right = e;//否则插入右边
        fixAfterInsertion(e);//对树进行自平衡
        size++;
        modCount++;
        return null;
    }

###1.4 元素插入后对树进行自平衡

红黑树的自平衡规则:(目标就是黑色节点平衡)

  • 每个节点都只能是红色或者黑色
  • 根节点是黑色
  • 每个叶节点(空节点)是黑色的。
  • 如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。
  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
    private void fixAfterInsertion(Entry x) {//x表示插入的节点,
        x.color = RED;//设置插入的节点为红色
        //当x不为空,x不为根节点,x的父节点为红色就需要进行平衡
        while (x != null && x != root && x.parent.color == RED) {
            //x的父节点等于x的爷爷节点的左边,其实就是说x的父节点是否属于左节点
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                //获取x的爷爷节点的右节点
                Entry y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {//爷爷节点的右节点为红色
                    setColor(parentOf(x), BLACK);//将x的父节点设置为黑色
                    setColor(y, BLACK);//x父节点的兄弟节点设置为黑色
                    setColor(parentOf(parentOf(x)), RED);//爷爷节点设置为红色
                    x = parentOf(parentOf(x));//x指向原来的爷爷节点,目的是为了继续往上修改颜色
                } else {//不为红色,也就是子节点和父节点的颜色出现冲突
                    //如果x等于父节点的右节点
                    if (x == rightOf(parentOf(x))) {
                        //操作的x为x的父节点
                        x = parentOf(x);
                        //左自旋,旋转后x为原来x的子节点
                        rotateLeft(x);
                    }
                    //x的父节点设置为黑色
                    setColor(parentOf(x), BLACK);
                    //x的爷爷节点设置为红色
                    setColor(parentOf(parentOf(x)), RED);
                    //右自旋
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                //y为爷爷节点的左节点(父节点的兄弟节点)
                Entry y = leftOf(parentOf(parentOf(x)));
                //如果y的颜色为红色
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);//父节点设置为黑色
                    setColor(y, BLACK);//y设置为黑色
                    setColor(parentOf(parentOf(x)), RED);//爷爷节点设置为红色
                    x = parentOf(parentOf(x));//x指向原来的爷爷节点,目的是为了继续往上修改颜色
                } else {
                    //如果父节点的左节点为x
                    if (x == leftOf(parentOf(x))) {
                        //x为父节点
                        x = parentOf(x);
                        //右自旋,旋转后x为原来x的子节点
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);//父节点设置为黑色
                    setColor(parentOf(parentOf(x)), RED);//爷爷节点设置为红色
                    rotateLeft(parentOf(parentOf(x)));//左自旋
                }
            }
        }
        root.color = BLACK;//保证根节点必须为黑色
    }

###1.5左自旋

    private void rotateLeft(Entry p) {
        //p不为空
        if (p != null) {
            //r表示p的右节点
            Entry r = p.right;
            //将r的左节点 赋值给 p的有右节点
            p.right = r.left;
            //如果r的左节点不为空
            if (r.left != null)
                //r的左节点的父节点为p
                r.left.parent = p;
            //r的父节点为p的父节点
            r.parent = p.parent;
            //如果p的父节点为空
            if (p.parent == null)
                //r作为根节点
                root = r;
            //如果p的父节点的左节点等于p,说明p为父节点的左节点
            else if (p.parent.left == p)
                //p的父节点的左节点为r
                p.parent.left = r;
            else
                //p的父节点的左节点为r
                p.parent.right = r;
            //r的左节点为p
            r.left = p;
            //p的父节点为r
            p.parent = r;
        }
    }

###1.6右自旋

 private void rotateRight(Entry p) {
        //p不为空
        if (p != null) {
            //l表示p的左节点
            Entry l = p.left;
            //p的左节点指向l的右节点
            p.left = l.right;
            //如果l的右节点不为空
            if (l.right != null)
                //l的右节点的父节点为p
                l.right.parent = p;
            //l的父节点指向p的父节点
            l.parent = p.parent;
            //如果p的父节点为空
            if (p.parent == null)
                //l为根节点
                root = l;
            //如果p的父节点的右节点为p
            else if (p.parent.right == p)
                //p的父节点的右节点为l
                p.parent.right = l;
            else
                //p的父节点的左节点为l
                p.parent.left = l;
            //l的右节点指向p
            l.right = p;
            //p的父节点指向l
            p.parent = l;
        }
    }

###1.7案例演示:
演示代码如下:
一起来看源代码-01TreeMap添加操作_第6张图片

代码执行步骤如下:

一起来看源代码-01TreeMap添加操作_第7张图片
一起来看源代码-01TreeMap添加操作_第8张图片
一起来看源代码-01TreeMap添加操作_第9张图片
一起来看源代码-01TreeMap添加操作_第10张图片
一起来看源代码-01TreeMap添加操作_第11张图片
一起来看源代码-01TreeMap添加操作_第12张图片
一起来看源代码-01TreeMap添加操作_第13张图片
一起来看源代码-01TreeMap添加操作_第14张图片
一起来看源代码-01TreeMap添加操作_第15张图片
一起来看源代码-01TreeMap添加操作_第16张图片
一起来看源代码-01TreeMap添加操作_第17张图片
一起来看源代码-01TreeMap添加操作_第18张图片
一起来看源代码-01TreeMap添加操作_第19张图片
一起来看源代码-01TreeMap添加操作_第20张图片
一起来看源代码-01TreeMap添加操作_第21张图片
一起来看源代码-01TreeMap添加操作_第22张图片
一起来看源代码-01TreeMap添加操作_第23张图片
一起来看源代码-01TreeMap添加操作_第24张图片
一起来看源代码-01TreeMap添加操作_第25张图片
21.png
一起来看源代码-01TreeMap添加操作_第26张图片
一起来看源代码-01TreeMap添加操作_第27张图片
一起来看源代码-01TreeMap添加操作_第28张图片
一起来看源代码-01TreeMap添加操作_第29张图片
一起来看源代码-01TreeMap添加操作_第30张图片
一起来看源代码-01TreeMap添加操作_第31张图片
一起来看源代码-01TreeMap添加操作_第32张图片
一起来看源代码-01TreeMap添加操作_第33张图片
一起来看源代码-01TreeMap添加操作_第34张图片
一起来看源代码-01TreeMap添加操作_第35张图片
一起来看源代码-01TreeMap添加操作_第36张图片
一起来看源代码-01TreeMap添加操作_第37张图片
一起来看源代码-01TreeMap添加操作_第38张图片
一起来看源代码-01TreeMap添加操作_第39张图片
一起来看源代码-01TreeMap添加操作_第40张图片
一起来看源代码-01TreeMap添加操作_第41张图片
一起来看源代码-01TreeMap添加操作_第42张图片
一起来看源代码-01TreeMap添加操作_第43张图片
一起来看源代码-01TreeMap添加操作_第44张图片
一起来看源代码-01TreeMap添加操作_第45张图片
一起来看源代码-01TreeMap添加操作_第46张图片
一起来看源代码-01TreeMap添加操作_第47张图片
一起来看源代码-01TreeMap添加操作_第48张图片
一起来看源代码-01TreeMap添加操作_第49张图片
一起来看源代码-01TreeMap添加操作_第50张图片
一起来看源代码-01TreeMap添加操作_第51张图片
一起来看源代码-01TreeMap添加操作_第52张图片
一起来看源代码-01TreeMap添加操作_第53张图片
一起来看源代码-01TreeMap添加操作_第54张图片
一起来看源代码-01TreeMap添加操作_第55张图片
52.png
一起来看源代码-01TreeMap添加操作_第56张图片
一起来看源代码-01TreeMap添加操作_第57张图片
一起来看源代码-01TreeMap添加操作_第58张图片
一起来看源代码-01TreeMap添加操作_第59张图片
一起来看源代码-01TreeMap添加操作_第60张图片
一起来看源代码-01TreeMap添加操作_第61张图片
一起来看源代码-01TreeMap添加操作_第62张图片
一起来看源代码-01TreeMap添加操作_第63张图片
一起来看源代码-01TreeMap添加操作_第64张图片
一起来看源代码-01TreeMap添加操作_第65张图片
一起来看源代码-01TreeMap添加操作_第66张图片
一起来看源代码-01TreeMap添加操作_第67张图片
一起来看源代码-01TreeMap添加操作_第68张图片
一起来看源代码-01TreeMap添加操作_第69张图片
一起来看源代码-01TreeMap添加操作_第70张图片
一起来看源代码-01TreeMap添加操作_第71张图片
一起来看源代码-01TreeMap添加操作_第72张图片
一起来看源代码-01TreeMap添加操作_第73张图片
一起来看源代码-01TreeMap添加操作_第74张图片
一起来看源代码-01TreeMap添加操作_第75张图片
一起来看源代码-01TreeMap添加操作_第76张图片
一起来看源代码-01TreeMap添加操作_第77张图片
一起来看源代码-01TreeMap添加操作_第78张图片
一起来看源代码-01TreeMap添加操作_第79张图片
一起来看源代码-01TreeMap添加操作_第80张图片
一起来看源代码-01TreeMap添加操作_第81张图片
一起来看源代码-01TreeMap添加操作_第82张图片
一起来看源代码-01TreeMap添加操作_第83张图片
一起来看源代码-01TreeMap添加操作_第84张图片
一起来看源代码-01TreeMap添加操作_第85张图片
一起来看源代码-01TreeMap添加操作_第86张图片

你可能感兴趣的:(Java基础,源代码,TreeMap,添加操作,底层原理,Java)