java TreeSet 和 TreeMap 源码解读

目录

一、前言

二、TreeSet详解

        1.TreeSet简介

        2.TreeSet的底层实现

                0° 准备工作

                1° TreeSet构造器

                2° 匿名内部类实现接口的多态

                3° TreeMap构造器

                4° add方法

                5° put方法和put方法

                6° 继续添加元素 

                7° 修改比较器的比较原则

三、TreeMap详解

        1.TreeMap简介

        2.TreeMap的底层实现

                0° 准备工作

                1° TreeMap构造器              

                2° add方法。

                3° 外层put和内层put

                4° 向集合添加第二个元素

                5° 改变判断元素的条件

四、完结撒❀


一、前言

        大家好,本篇博文将通过Debug流程分析,带大家仔细剖析一下TreeSet以及TreeMap的底层实现。TreeSet的底层其实就是TreeMap,见名知意,与树有关,鉴于二者的底层都涉及到了较多数据结构的内容,目前在过渡阶段,up就给大家把重点内容过一下就行,当然还是比一般地方讲得要细的。
        注意 : 解读源码需要扎实的基础,比较适合希望深究的同学; 不要眼高手低,看会了不代表你会了,自己动手跟着过一遍才算有收获; 点击文章的侧边栏目录或者前面的目录可以进行跳转。 良工不示人以朴,up所有文章都会适时进行补充完善。大家有问题都可以在评论区讨论交流,或者私信up。 感谢阅读!


二、TreeSet详解

        1.TreeSet简介

                TreeSet是Set接口的一个实现类,其类图如下 :

                TreeSet中的元素不能为null,否则会报NullPointerException。                
                与HashSet实现类不同,TreeSet最大的特点是可以进行排序。TreeSet底层是二叉树,可以对对象元素进行排序,但是自定义类需要实现comparable接口,可以使用匿名内部类实现该接口,并在匿名内部类中重写compare方法。

        2.TreeSet的底层实现

                0° 准备工作

                为了通过Debug,结合源码来分析TreeSet的底层,up以TreeSet_Demo类为演示类,代码如下 : (main函数第一行设置断点)

package csdn.knowledge.api_tools.gather.set;

import java.util.Comparator;
import java.util.TreeSet;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class TreeSet_Demo {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).compareTo((String)o2);
            }
        });

        treeSet.add("141");
        treeSet.add("5");
        treeSet.add("23");
        treeSet.add("114514");

        System.out.println("treeSet = " + treeSet);
    }
}

                1° TreeSet构造器

                进入Debug界面,首先我们跳入TreeSet的带参构造,如下

                可以看到,TreeSet底层的确调用了TreeMap。我们先不急着跳入TreeMap的构造器,先来看看TreeSet带参构造的形参——一个Comparator(比较器)类型的变量,这个Comparator类型其实就是一个接口其源码如下

                2° 匿名内部类实现接口的多态

                而我们一开始传入的这个玩意儿——

        new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).compareTo((String)o2);
            }
        }

                其实就是一个实现了该接口的匿名内部类对象,并在TreeSet带参构造中形成了多态。而在TreeSet带参构造中,又将匿名内部类对象comparator传递给了TreeMap的一个带参构造。
                匿名内部类中实现了Comparator()接口中的compare方法,该方法决定了TreeSet集合中元素排序的原则。此时,排序的规则是由String类的compareTo方法决定的,我们来简单回顾一下String类的compareTo方法如下——

        int compareTo(String anotherString)——

        返回两个字符串对象的比较结果——若相等,返回0;若不相等,从两个字符串的第一个字符开始比较,返回第一个不相等的字符的ASCII码差值。当较长字符串的前面部分恰巧是较短的字符串时,返回两个字符串的长度差。

                因此,根据此规则,当前TreeSet集合中的元素应该是按字母表顺序来排列的,比方说我现在传入了两个字符串o1和o2,分别为"ABC"和"BBC",那么此时compareTo方法的返回值就是A的ASCII码值 - B的ASCII码值 = 65 - 66 = -1,而-1是小于0的;那么在使用add方法添加元素时,compareTo方法的这个返回值就决定了添加元素时的排列顺序,关于这一点,在下面的add方法中可以体现。

                3° TreeMap构造器

                好的,我们接下来再追入TreeMap的带参构造看看,如下图所示 : 

                可以看到,在TreeMap构造器中,又将匿名内部类对象comparator传递给了TreeMap的一个属性comparator
                并且,此时的comparator已经显示为了匿名内部类“TreeSet_Demo$1”的形式。
                接下里我们跳出构造器,逐层返回到演示了中。

                4° add方法

                像TreeSet集合中添加第一个元素"141",跳入add方法,如下所示 : 

                可以看到,底层仍然走的是put方法;注意实参中的PRESENT, 这个PRESENT就和HashSet中PRESENT是一个作用了,仅仅是作为一个空对象,起到占位的作用,其源码如下 : 

                5° put方法和put方法

                继续跳入put方法,如下所示 : 

                经典的“包皮”结构, 继续跳入内层put方法,如下 : 

                内层的put方法代码是非常多并且复杂的,里面涉及到许多数据结构的知识。还好,这是第一次添加元素,直接进入第一个if语句就return出去了😂。

                当然,很明显真正完成添加元素的操作是在addEntryToEmptyMap方法中进行的。我们进去稍微瞅一眼,如下图所示 : 

                可以看到,TreeSet是把数据包装成了Entry类型来进行存放的,这里的root是TreeMap中的一个Entry类型的引用变量。如下 : 

                并且,这里的Entry类型(TreeMap中的)其实和Hashtable中的Entry类型一样,都实现了Map接口中的Entry内部接口,如下图所示 : 

                6° 继续添加元素 

                继续添加元素,直接快进到内层put方法,如下 : 

                这时候t显然不为空,不进入第一个if语句。重点是第二个if语句,如下: 

                仔细观察,显然它是根据cmp(匿名内部类中重写的compare方法的返回值)的值来进行比较,从而决定添加元素的相应操作。而且,根据动态绑定机制,这时候如果我们跳入compare方法,一定会跳到之前的匿名内部类, 如下图所示 : 

                好的,多的也不瞎扯了,讲太深也没几个人能看懂。接下里我们回到演示类,并执行完毕剩余语句,输出结果如下 :  

                可以看到,的确是由ASCII码值从小到大进行排列(注意compareTo比较的方式)。  

                7° 修改比较器的比较原则

                除了compareTo方法,我们还可以修改compare方法return语句中的内容,如下所示  :

        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).length() - ((String)o2).length();
            }
        });

                如此一来, 判断的条件就改成了“两字符串的长度是否相同”,如果相同则无法添加。为进行对比,我们先在之前的判断条件(compareTo方法的判断)下修改add方法的实参,如下 : 

                输出结果 : 

                然后,我们再以新的判断条件运行,运行结果如下 : 

                可以看到,与"5"长度相同的"3","2";与"141"长度相同的"233"都没法添加到集合中。 
                这时候,我们查看Debug界面,可以看到"5"和"141"都已经挂载到了root下面,如下图所示 : 


三、TreeMap详解

        1.TreeMap简介

                1° TreeMap作为Map接口的一个实现类,也是保存和记录key-value的映射关系——键值对的,其类图如下 : 

                TreeMap中,key不可以为null,否则会报NullPointerException但是value可以为null
                TreeMap也可以对元素进行自定义排序,实现方式与TreeSet一致

        2.TreeMap的底层实现

                0° 准备工作

                up仍是结合源码给大家分析一下,以TreeMap_Demo类为演示类代码如下 : (main函数第一行设置断点)

package csdn.knowledge.api_tools.gather.map;

import java.util.Comparator;
import java.util.TreeMap;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class TreeMap_Demo {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).compareTo((String)o2);
            }
        });

        treeMap.put("Alice", "girl");
        treeMap.put("Bob", "boy");
        treeMap.put("Cyan", "boy");

        System.out.println("treeMap = " + treeMap);
    }
}

                1° TreeMap构造器              

                同样地,先跳入 TreeMap的构造器看看,如下 : 

                还是那老一套,将实现了Comparator接口的匿名内部类对象传递给TreeMap的comparator属性

                2° add方法。

                向集合添加第一个元素,跳入add方法,如下 : 

                可以看到,此时的value值已经不是PRESENT了,而是传入的实参girl

                3° 外层put和内层put

                跳入外层put,如下 : 

                继续,跳入内层put方法

                可以看到,和TreeSet基本就一套,没啥讲得😂。 

                仍然是在addEntryToEmptyMap方法中完成了对第一个元素的添加。         

                4° 向集合添加第二个元素

                仍然是进入内层put方法中进行比较,如下图所示 : 

                这时候,我们跳入compare方法,根据动态绑定机制,一定会跳到匿名内部类中,如下图所示 : 

                OK,接下来还是会根据compare方法的返回值进行判断,执行对应的添加操作。这里就不再演示了,我们将三个元素全部添加进去,如下图所示 : 

                可以看到,三个键值对全部成功挂载到了root下。 

                5° 改变判断元素的条件

                同样地,可以通过修改compare方法return语句中的内容来改变添加元素时的判断条件,如下所示 : 

        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //return ((String)o1).compareTo((String)o2);
                return ((String)o1).length() - ((String)o2).length();
            }
        });

                同时,我们将添加的第二个元素的key由"Bob" 改为"Peter",这时候,由于"Peter"与"Alice"的长度一样,所以第二个元素是无法成功加入集合的,如下图所示 : 


四、完结撒❀

        🆗,以上就是TreeSet和TreeMap的源码解读了,其实就是把TreeMap讲了两遍😂。当然,up尽量避重就轻,源码中涉及到数据结构的部分基本都没讲,关于红黑树,或者其他数据结构与算法的知识,up将来会单独出一个专栏,用Java语言来描述讲解,大家敬请期待。感谢阅读!

        System.out.println("END-----------------------------------------------------------------------------"); 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

拥有良好的社交和友谊会使肠道微生物群更健康

谷禾健康 播种肠道,喂养心灵 在新冠疫情的影响下,我们的生活方式和社交模式都发生了很大的改变。随着社交距离的要求和封锁措施的实施,我们不得不放弃了很多与朋友和家人的互动,这给我们的身心健康带来了很大的影响。 然而&#x…

区块链学习笔记(3)BTC协议

假设有一个大家都信任的中心化机构想要发行数字货币。 该机构由用自己的私钥签名后后发行,任何人都可以通过公钥验证该货币是否为真。 买东西的时候,购买者可以将数字货币发送给卖方,卖方可以也可以通过公钥验证该货币为真后即可完成支付的过…

子网掩码和CIDR

CIDR是什么 网络标识相同的计算机必须同属于同一个链路。例如,架构B类IP网络时,理论上一个链路内允许6万5千多台计算机连接。然而,在实际网络架构当中,一般不会有在同一个链路上连接6万5千多台计算机的情况。因此,这种…

蓝桥杯刷题冲刺 | 倒计时7天

作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾最后一周,复习学过的知识,刷题冲刺🐾 文章目录1.高精度除法2.扫地机器人3.数的范围4.A-B 数对1.高精度除法 题目 链接: 794. 高精度除法 - AcWing题…

Java对象内存布局

文章目录1、对象头对象标记Mark Word类元信息(又叫类对象指针)Class Pointer数组长度(Array Length)(可选)2、实例数据(对象体)3、对齐填充4、指针压缩5、再聊对象头的MarkWord6、JO…

Android ART虚拟机 Space类体系

前言 在ART虚拟机实现中,内存分配和释放的算法是封装在不同的Space中来完成的。而外部使用者只能借助Space及派生类的接口来完成内存的分配与释放。通过阅读这些Space的实现,可以看出ART虚拟机的一个重要的特点就是大量使用映射内存,相较于D…

思维导图软件哪个好?安利八款好用的思维导图软件

当你需要表达和整理复杂的想法、计划和项目时,思维导图软件可以是非常有用的工具。不同的思维导图软件有不同的功能和特点,选择适合自己的软件可以让你更高效地工作和学习。但是你了解思维导图软件哪个好呢?下面就给大家安利八款简单好用的思…

分享99个ASP影音娱乐源码,总有一款适合您

分享99个ASP影音娱乐源码,总有一款适合您 99个ASP影音娱乐源码下载链接:https://pan.baidu.com/s/1pYpAqFUX0xD8KR8GDRyiug?pwd3lja 提取码:3lja Python采集代码下载链接:采集代码.zip - 蓝奏云 我的博客地址:亚…

1Panel开源面板项目GitHub Star数量突破2,000!

截至2023年4月4日18:00,FIT2CLOUD飞致云旗下开源项目——1Panel开源Linux服务器运维管理面板GitHub Star数超过2,000个!

IDE装上ChatGPT,一天开发一个系统

昨天白天在写代码,晚上看了一场直播,是两个技术的直播: 一个是技术总监,一个是号称Java之父的余**。 结果Java之父被技术总监吊打。然后匆匆下播。 技术这玩意,真的就是真的! 白天我开发了一个系统&…

LeetCode.每日一题 2427. 公因子的数目

Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…

ModuleNotFoundError: No module named ‘gdal‘

目录 一、问题描述 二、解决方法 一、问题描述 在win系统下使用gdal包的时候,使用下面代码pip安装: conda install glob -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ 安装过程中没有报错,但是 import 的时候还是报错了…

Vicuna:与ChatGPT 性能最相匹配的开源模型

Vicuna (由stable diffusion 2.1生成)前言最近由UC Berkeley、CMU、Stanford, 和 UC San Diego的研究人员创建的 Vicuna-13B,通过在 ShareGPT 收集的用户共享对话数据中微调 LLaMA获得。其中使用 GPT-4 进行评估,发现Vicuna-13B 的性能达到了ChatGPT 和 …

脑外伤最怕后遗症?做好这6大家庭护理措施,防止后遗症

脑外伤是生活中常见的一种情况,主要也就是由于意外或者是其他原因造成的脑部外伤。脑外伤也属于神经系统疾病的一种,最主要是因为对脑部的组织细胞以及神经造成了巨大伤害,从而引起的一系列不良症状的疾病,这种时候也就需要做护理…

憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台

憨批的语义分割重制版11——Keras 搭建自己的HRNetV2语义分割平台学习前言什么是HRNetV2模型代码下载HRNetV2实现思路一、预测部分1、主干网络介绍a、Section-1b、Section-2c、Section-3d、Section-42、特征整合部分3、利用特征获得预测结果二、训练部分1、训练文件详解2、LOSS…

python123

文章目录温度转换异常处理百分制成绩转换五分制F正整数AB奇偶求和判断数据类型温度转换异常处理 描述‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪…

数组切分 蓝桥杯 DFS DP

⭐ 数组切分 输入 4 1 3 2 4输出 5⭐ 区间最大值 - 区间最小值 区间长度:说明该区间为连续的自然数 🤠 暴馊dfs (过 50 % 的案例) import java.util.*;public class Main {static int mod 1000000007, n;static int res 0…

OBCP第七章 OB迁移-备份恢复技术架构及操作方法

为什么需要备份恢复 为满足监管要求 防止管理员误操作后,错误数据同步到所有副本,导致数据无法恢复 防止数据库因各种故障而造成数据丢失,降低灾难性数据丢失的风险,从而达到灾难恢复的目的 硬盘驱动器损坏 黑客攻击、病毒 …

九龙证券|券商积极布局A股 新进171家公司前十大流通股东

随着上市公司2022年年报连续出炉,券商自营盘的操作途径同步浮现。Choice数据显示,到4月4日,券商新进171家上市公司前十大流通股东之列,有48家上市公司获加仓。从装备方向来看,制造业、非银金融、电子、电力设备等板块依…

【电源专题】充电过程中指示灯怎么就红绿闪烁或是同时亮

本案例是在工作中发现的,同事测试的时候发现产品充电时指示灯红绿闪烁。正常的表现应该是充电为红灯,充满为绿灯。怎么会出现红绿闪烁的情况呢? 首先前提条件是产品已经量产,并且出货数量巨大,因此是否为个例性问题?通过排查后发现,最终现象是跟着充电器走,使用正常产品…