TreeMap的底层实现

news2024/11/16 11:28:48

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/806094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot配置加密实践

Spring Boot配置加密实践 使用Java技术栈的时候&#xff0c;Spring Boot几乎已经成为了标配。Spring Boot帮助我们简化了各种技术的整合&#xff0c;我们只需要在application.yml配置文件中增加一点点的配置即可。 虽然Spring Boot简化了我们的工作&#xff0c;但是也隐藏了底…

draw up a plan

爱情是美好的&#xff0c;却不是唯一的。爱情只是属于个人化的感情。 推荐一篇关于爱情的美文&#xff1a; 在一个小镇上&#xff0c;有一家以制作精美巧克力而闻名的手工巧克力店&#xff0c;名叫“甜蜜之爱”。这家巧克力店是由一位名叫艾玛的年轻女性经营的&#xff0c;她对…

【Git】版本回退与撤销修改案例

目录 一、版本回退 二、撤销修改案例 案例1&#xff1a;仅在工作区进行了修改还未进行add操作 案例2&#xff1a;仅进行了add 操作还未进行commit操作 案例3&#xff1a;进行了add与commit操作无其他操作 三、版本库中删除文件 一、版本回退 在进行版本回退之前我们需要…

类加载机制与类加载器

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ Java 源码是如何形成类文件的&#xff0c;类文件又是如何加载到虚拟机的&#xff0c;类加载有哪些机制和原则呢&#xff1f;本文将为大家一一介绍。 1 Java 源码形成类文件…

音乐编曲软件FL Studio21中文免费版新功能及下载

FL Studio21是一款数字音频工作站软件&#xff0c;用于音乐制作和混音。它具有广泛的音频编辑和处理功能&#xff0c;可以帮助你创建出专业的音乐和音频内容。它还支持多个音频和效果轨道&#xff0c;可以帮助你创建更加复杂和高质量的音频内容。FL Studio是一款著名的DAW音频制…

1300*C. A Cookie for You

Example input 6 2 2 1 2 0 100 0 1 12 13 25 1 27 83 14 25 0 0 1 0 1000000000000000000 1000000000000000000 1000000000000000000 1000000000000000000 output Copy Yes No No Yes No Yes 解析&#xff1a; 因为第二种人只能吃少的那种蛋糕&#xff0c;所以优先满足他…

Flutter 开发者工具 Android Studio 开发Flutter应用

Flutter 开发者工具 在 Android Studio 开发Flutter应用 &#x1f525; Android Studio 版本更新 &#x1f525; Android Studio Check for Update Connection failed ​ 解决方案 如果是运行的是32位的android studio需要在andriod studio的启动目录下找到studio.exe.vmoptio…

spring boot 2 配置上传文件大小限制

一、起因&#xff1a;系统页面上传一个文件超过日志提示的文件最大100M的限制&#xff0c;需要更改配置文件 二、经过&#xff1a; 1、在本地代码中找到配置文件&#xff0c;修改相应数值后交给运维更新生产环境配置&#xff0c;但是运维说生产环境没有这行配置&#xff0c;遂…

Java那些“锁”事 - 死锁及排查

死锁是两个或者两个以上的线程在执行过程中&#xff0c;因争夺资源而造成的一种互斥等待现象&#xff0c;若没有外界干涉那么它们将无法推进下去。如果系统资源不足&#xff0c;进程的资源请求都得到满足&#xff0c;死锁出现的可能性就很低&#xff0c;否则就会因为争夺有限的…

kali安装Docker的方法+一次错误体验(第一行错误)

一.kali安装docker #添加docker的gpg密钥&#xff0c;签名用的 curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian/gpg | sudo apt-key add -#添加docker的apt源 echo deb https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian/ buster sta…

使用Jetpack Compose和Motion Layout创建交互式UI

使用Jetpack Compose和Motion Layout创建交互式UI 通过阅读本博客&#xff0c;您将学会使用Motion Layout实现这种精致的动画效果&#xff1a; 让我们从简单的介绍开始。 介绍 作为Android开发者&#xff0c;您可能会遇到需要布局动画的情况&#xff0c;有时甚至需要变形样…

Linux安装kafka3.5.1

要在Ubuntu上安装Apache Kafka&#xff0c;请按照以下步骤操作&#xff1a; 1、安装Java运行时环境(Ubuntu)&#xff1a; 如果已经安装jdk不用执行 sudo apt update sudo apt install default-jre2、下载Kafka&#xff1a; wget https://downloads.apache.org/kafka/3.5.1/…

Docker容器基本操作之启动-停止-重启

一、安装启动RabbitMQ容器 此处以rabbitmq容器为例 前提&#xff1a;需要安装配置好docker(设置镜像源、配置阿里云加速)、开启docker&#xff0c;停止(stop)或者禁用(disable)手动解压缩安装的rabbitmq,以防与docker中安装的rabbitmq冲突。 //查看docker状态 systemctl stat…

android Glide加载gif动图和本地视频,Java

droid Glide加载gif动图和本地视频&#xff0c;Java //从手机存储本地加载视频 String filePath "/storage/emulated/0/Pictures/my_video.mp4"; Glide .with( context ).load( Uri.fromFile( new File( filePath ) ) ).into( imageView );//加载gif Glide .with(…

用群晖NAS搭建个人音乐库

安装教程 勾选启动NTP服务 1. 群晖安装Audio Station 2. filestation会生成一个music文件夹&#xff0c;把下载好的音乐丢进music即可&#xff08;音乐平台听不到的歌也顺带通过下载解决了&#xff09;这时候你就可以在audio station直接听歌了不过咱们大部分时候听歌是在手机…

恒运资本:深市半年报预告规定?

依据深圳证券交易所等法律法规和业务规范的要求&#xff0c;应当及时、公平、精确发表成绩预告、成绩快报、中报、年度报告等信息。涉及中长期成绩情况的&#xff0c;还应当发表未来发展计划、运营战略、面对的风险与应战以及处理措施等中长期内容。 半年报成绩预告发表规则&am…

【PGMPY】 1. DAG基础结构

pgmpy 贝叶斯网络的纯python实现&#xff0c; 用途&#xff1a; 结构学习、 参数估计、 近似&#xff08;基于采样&#xff09; 精确推理 因果推理 安装 pip install pgmpyconda install -c ankurankan pgmpyconda install -c ankurankan pgmpy文档 https://pgmpy.org/index…

C++多线程编程(包含c++20内容)

C多线程编程(包含c20内容) 文章目录 C多线程编程(包含c20内容)线程通过函数指针创建线程通过函数对象创建线程通过lambda创建线程通过成员函数创建线程线程本地存储取消线程自动join线程从线程获得结果 原子操作库原子操作原子智能指针原子引用使用原子类型等待原子变量 互斥互…

Java【Spring】项目创建、存储和获取 Bean 的基本方式

文章目录 前言一、创建 Spring 项目1, 创建 Maven 项目2, 添加 Spring 依赖3, 创建启动类 二、存储 Bean 的基本方式1, 创建 Bean2, 存储 Bean 三、获取 Bean 的基本方式1, 获取上下文对象2, 获取 Bean3, 使用 Bean 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的…

tinkerCAD案例:18. Glow Circuit Assembly 发光电路组件

tinkerCAD案例&#xff1a;18. Glow Circuit Assembly 发光电路组件 In this tutorial, we’ll walk through how to use the Glow circuit assembly. 在本教程中&#xff0c;我们将演练如何使用 Glow 电路程序集。 Using the Glow circuit assembly, you’ll build a pengui…