TreeMap的底层实现

0. 你需要知道的TreeMap的内置属性

0.1 节点属性

K key;					// 键
V value;				// 值
Entry<K,V> left;		// 左子节点
Entry<K,V> right;		// 右子节点
Entry<K,V> parent;		// 父节点
boolean color;			// 节点的颜色

0.2 成员变量

    //比较器对象
    private final Comparator<? super K> comparator;

	//根节点
    private transient Entry<K,V> root;

	//集合的长度
    private transient int size = 0;

1. TreeMap的几种构造方法

1.1 空参构造

空参构造
解释:当我们进行空参初始化TreeMap时,TreeMap底层只做了一件事,就是初始化比较器为空

1.2 带比较器参数的构造方法

带比较器参数的构造方法
解释:当我们进行初始化TreeMap时,传入了一个比较器对象,TreeMap底层也只做了一件事,就是将你传入的比较器对象赋值给当前的comparator对象

1.3 带SortedMap参数的构造函数

带SortedMap参数的构造函数
解释:当我们初始化TreeMap的时候,传入了一个SortedMap对象,TreeMap底层做了两件事,一是将SortedMap中的比较器对象赋值给当前TreeMap的比较器,然后根据SortedMap的规则进行转换为TreeMap

1.4 带Map参数的构造函数

带Map参数的构造函数
解释:当初始化TreeMap时,传入了一个Map对象(非SortedMap对象),TreeMap底层做了两件事,一是置空比较器,二是将当前map中的数据存入TreeMap中

2. put方法

2.1 源码

    private V put(K key, V value, boolean replaceOld) {
        Entry<K,V> t = root;
        if (t == null) {
            addEntryToEmptyMap(key, value);
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        } else {
            Objects.requireNonNull(key);
            @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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
        addEntry(key, value, parent, cmp < 0);
        return null;
    }

2.2 逐行分析

	private V put(K key, V value, boolean replaceOld) {
		//获取根节点的地址值,赋值给局部变量t
        Entry<K,V> t = root;
		//判断根节点是否为null
		//如果为null,表示当前是第一次添加,会把当前要添加的元素,当做根节点
		//如果不为null,表示当前不是第一次添加,跳过这个判断继续执行下面的代码
        if (t == null) {
			//方法的底层,会创建一个Entry对象,把他当做根节点
            addEntryToEmptyMap(key, value);
			//表示此时没有覆盖任何的元素
            return null;
        }
		//表示两个元素的键比较之后的结果
        int cmp;
		//表示当前要添加节点的父节点
        Entry<K,V> parent;
		
		//表示当前的比较规则
		//如果我们是采取默认的自然排序,那么此时comparator记录的是null,cpr记录的也是null
		//如果我们是采取比较去排序方式,那么此时comparator记录的是就是比较器
        Comparator<? super K> cpr = comparator;
		//表示判断当前是否有比较器对象
		//如果传递了比较器对象,就执行if里面的代码,此时以比较器的规则为准
		//如果没有传递比较器对象,就执行else里面的代码,此时以自然排序的规则为准
        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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        } else {
			//把键进行强转,强转成Comparable类型的
			//要求:键必须要实现Comparable接口,如果没有实现这个接口
			//此时在强转的时候,就会报错。
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
				//把根节点当做当前节点的父节点
                parent = t;
				//调用compareTo方法,比较根节点和当前要添加节点的大小关系
                cmp = k.compareTo(t.key);
				
                if (cmp < 0)
					//如果比较的结果为负数
					//那么继续到根节点的左边去找
                    t = t.left;
                else if (cmp > 0)
					//如果比较的结果为正数
					//那么继续到根节点的右边去找
                    t = t.right;
                else {
					//如果比较的结果为0,会覆盖
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
		//就会把当前节点按照指定的规则进行添加
        addEntry(key, value, parent, cmp < 0);
        return null;
    }	
	
	 private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (addToLeft)
            parent.left = e;
        else
            parent.right = e;
		//添加完毕之后,需要按照红黑树的规则进行调整
        fixAfterInsertion(e);
        size++;
        modCount++;
    }
	private void fixAfterInsertion(Entry<K,V> x) {
		//因为红黑树的节点默认就是红色的
        x.color = RED;

		//按照红黑规则进行调整
		
		//parentOf:获取x的父节点
		//parentOf(parentOf(x)):获取x的爷爷节点
		//leftOf:获取左子节点
        while (x != null && x != root && x.parent.color == RED) {
			
			
			//判断当前节点的父节点是爷爷节点的左子节点还是右子节点
			//目的:为了获取当前节点的叔叔节点
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
				//表示当前节点的父节点是爷爷节点的左子节点
				//那么下面就可以用rightOf获取到当前节点的叔叔节点
                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 {
				//表示当前节点的父节点是爷爷节点的右子节点
				//那么下面就可以用leftOf获取到当前节点的叔叔节点
                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;
    }
	

说明:本文大部分代码说明来自黑马程序员b站阿伟视频,在这里力推b站黑马程序员Java从入门到起飞课程,b站直达:https://www.bilibili.com/video/BV17F411T7Ao/?spm_id_from=333.337.search-card.all.click

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Treemap是一种基于红黑树实现的数据结构。它将key-value键值对按照key的大小关系,以一种类似于地图等级分区的方式分成不同层级,每个键值对以叶子节点的形式存储在最底层。在每个非叶子节点中,以所包含子节点的key值的大小为权值大小,通过一些算法进行子节点的分配和聚合,最终形成一棵树状结构。 Treemap底层实现使用了红黑树,红黑树是一种自平衡的二叉查找树。在红黑树中节点被标记为红色或者黑色,它们的连接被称为边。通过一些规则保证红黑树的平衡,使得树的高度二对数级别。 在Treemap中对红黑树进行了扩展,每一个节点不仅记录自己的key值和value值,还记录自己的size值,即以这个节点为根的子树的大小。同时,Treemap在插入和删除节点时会维护根节点的左右子节点,并更新每个节点的size值。这样就可以使得Treemap支持一些特殊的操作,如下图所示: ![treemap](https://images0.cnblogs.com/i/429060/201405/292026107062058.jpg) 在Treemap中,可以通过一些算法实现数据的索引和查找操作,如下: 1. 根据key值查找对应的value值:首先,在红黑树中找到与key值最接近的节点,然后再与目标节点比较key值,确定目标节点是否存在。 2. 根据value值查找对应的key值:遍历整颗红黑树,直到找到对应的value值,然后返回对应的key值。 3. 实现排序功能:从Treemap中获取一个Iterator对象,然后通过遍历Iterator对象实现排序。 Treemap底层实现是红黑树,它的时间复杂度为O(logn),因此,Treemap非常适合需要快速查找和排序大量数据的情况。同时,Treemap由于要维护树状结构,因此相对于HashMap等数据结构,它的空间复杂度要大一些。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kkuil

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

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

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

打赏作者

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

抵扣说明:

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

余额充值