TreeMap和TreeSet的介绍

news2024/11/15 7:58:06


 

目录

1、认识 TreeMap 和 TreeSet

2、TreeMap 的主要成员变量

3、TreeMap 的主要构造方法

4、TreeMap 和 TreeSet 的元素必须可比较

5、TreeMap 和 TreeSet 关于 key 有序 

6、TreeMap 和 TreeSet 的关系 

7、总结


1、认识 TreeMap 和 TreeSet

TreeMap 和 TreeSet 是Java中利用搜索树实现的 Map 和 Set,它们的底层是红黑树,而红黑树是一棵近似平衡的二叉搜索树,关于红黑树相关知识后续讲解。本期主要是学会 TreeMap 和 TreeSet 的使用,以及知道他们的特点即可。


2、TreeMap 的主要成员变量

// 存储传入比较器的引用
private final Comparator<? super K> comparator;

// 搜索树的根节点
private transient Entry<K,V> root;

// 节点个数
private transient int size = 0;

// 统计搜索树结构修改的次数
private transient int modCount = 0;

这里我们需要注意的是 comparator 这个引用,它是用来接收一个比较器的,主要功能后续会讲解,这里注意一下即可。


3、TreeMap 的主要构造方法

public TreeMap() {
    comparator = null;
}

public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}
  • 第一个构造方法,没什么意外的,你没传比较器嘛,自然就是 null。
  • 第二个是传了比较器的构造方法, 指定了比较器也很简单。
  • 第三个构造方法, 则是把你传递的 Map 构造一个新的树映射,包含与给定映射相同的映射,并根据其 key 的自然顺序进行排序,这些 key,必须可相互比较!

4、TreeMap 和 TreeSet 的元素必须可比较

因为 TreeMap 和 TreeSet 实现了 SortedSet 接口,表示是一个需要实现排序功能的 Map 或 Set,那实现排序的前提,你放入的元素必须是可比较的,那么也就是说,当你往 TreeMap 里面放 key 的时候,这个 key 必须可比较,也就是重写了 compaerTo 方法,你也可也直接传一个比较器也是可以的。

public class Test {
    public static void main(String[] args) {
        Map<Person, Integer> map = new TreeMap<>();
        System.out.println(map);
    }
}

这个报错就是在说, Person 无法被转换成 Comparable,也就是在 TreeMap 底层实现中无法将 key 对象中的 compareTo 方法。

具体我们还是要去看 TreeMap 的无参构造方法以及 put 的源码:

public TreeMap() {
    comparator = null;
}

对于 TreeMap 的无参构造方法,其实很简单,如果你没有传比较器进去,默认就是 null 的,接下来就得看一下 put 方法了。

源码中比较多,我们这里只截取一小部分,能明白为啥重写 compareTo 方法即可。

很明显,我们是第一次 put 元素,所以搜索树的根节点也就是 root 节点为 null,接着将我们 put 进去的 key 作为 compare 两个参数传递进去,接着去看 compare 的实现:

这里我们没有传比较器,所以刚开始的无参构造方法已经将 comparator 置 null了,很明显这里可以发现,如果我们传了比较器,就按照比较器的方式来比较,如果没有比较器,则会将 key 转换成 Comparable<Person>,这个尖括号中的类型, 此时我们这里是转换成 Person,这也是泛型上界的相关知识,所以即最终调用了我们 key 对象中的 compareTo 方法,那如果我们没有实现 Comparable 这个接口,也没有重写 compareTo 方法自然会抛异常。

当然后续 put 元素也是按照上面的方法来比较的,有比较器,使用比较器来比较,无比较器,调用对象中的 compareTo 进行比较。


5、TreeMap 和 TreeSet 关于 key 有序 

我们前面讲到过, TreeMap 和 TreeSet 的底层其实是搜索树,而且是红黑树,那么中序遍历搜索树是有序的,也即按照 key 提供的比较方式,或者你自己提供的比较器,关于 key 是有序的。

class Person implements Comparable<Person> {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Person o) {
        return this.age - o.age;
    }
}

public class Test {
    public static void main(String[] args) {
        TreeMap<Person, Integer> map = new TreeMap<>();
        map.put(new Person("张三", 12), 1);
        map.put(new Person("李四", 21), 2);
        map.put(new Person("王五", 16), 3);
        Set<Map.Entry<Person, Integer>> entrySet = map.entrySet();
        for (Map.Entry<Person, Integer> personIntegerEntry : entrySet) {
            System.out.println(personIntegerEntry);
        }
    }
}

由于 Map 没有继承于Iterable 接口,所以不能采用 for-each 遍历,只能返回 Key-value 的映射关系放入 Set 中进行遍历。

上述代码是按照 Person 中的年龄进行比较的,所以如果最终打印出来的结果是 12 21 16 这样的年龄排序的顺序话,也足以说明,在 TreeMap 和 TreeSet 中是关于 key 有序的,打印结果:

看到这,可能有的小伙伴说,确实是关于 key 有序,但是怎么保证底层是一棵搜索树呢?其实也很简单验证,搜索树前几期也讲过,如果是按照我们 Person 类比较的方式的话,那么根节点的左边都小于它,根节点的右边都大于它,这里我们通过调试看看 map 中存储的结构就ok了:

这里我们通过调试的方式进入到源码中,输入我们想观察的变量,于是可以看到,确实是一棵搜索树,而且不是简单的二叉搜索树,是一棵红黑树。

为什么是这里就能看出来是红黑树呢,因为如果是按照我们前面讲二叉搜索树的逻辑, 根节点一定是第一次插入的 “张三,12”,而这里跟节点为什么是 “王五,16”,因为当插入 “王五” 的时候,搜索树的左右子树高度不平衡了,红黑树进行了旋转调整,至于更多底层实现细节,目前我们可以不用关心,由此也可能看出来并不是一棵简单的二叉搜索树。


6、TreeMap 和 TreeSet 的关系 

上期其实也提到过,Set 的底层其实就是 Map,而 TreeSet 也是一样,底层仍然是 TreeMap,拿什么证明呢?其实我们来看 Set 的构造方法就可以了:

TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
}

public TreeSet() {
    this(new TreeMap<E,Object>());
}

public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}

public TreeSet(Collection<? extends E> c) {
    this();
    addAll(c);
}

这里的 NavigableMap 也是一个继承 SortedMap 的接口,因此具有SortedMap,Map接口的属性方法,通过上述的构造方法也能看出,当你实例化一个 TreeSet 对象的时候,本质上还是 new 了一个 TreeMap 对象。而能明显看到,value 为一个 Object 默认对象。


7、总结

TreeMap 和 TreeSet 底层都是红黑树,插入删除查找的时间复杂度为 O(logN),数据关于 key 是有序的,key 必须要能够比较,不然会抛出 ClassCastException 异常,主要运用于需要 key 有序的场景下,TreeMap 和 TreeSet 是线程不安全的。 


下期预告:【Java 数据结构】HashMap和HashSet

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

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

相关文章

探索SpringMVC-组件之ViewResolver

前言 ViewResolver也就是视图解析器&#xff0c;他将是我们《探索SpringMVC》系列要介绍的最后一个常用的组件。其他组件&#xff1a;MultipartResolver、LocaleResolver、ThemeResolver、RequestToViewNameTranslator、FlashMapManager&#xff0c;相对简单&#xff0c;大家可…

一个想活得简单的程序猿的2022年终总结!

前言 今年的总结相比以往来说&#xff0c;可写的太少了&#xff0c;但看到我17年开始写的年终总结&#xff0c;已定下每年写下的承诺&#xff0c;因此即便可写的不多&#xff0c;但是还是写下吧&#xff0c;毕竟又过了一年&#xff0c;总有东西会留下&#xff01; 今年事件 疫…

【Linux杂篇】Windows远程登陆Linux、Linux静态IP配置

前言 如果要长期连接Linux环境&#xff0c;就需要给Linux配置一个静态IP&#xff0c;否则可能每次连接的IP都不一样而且还很麻烦。 除此之外&#xff0c;我们使用ssh远程登录的时候&#xff0c;每次都要输入密码&#xff0c;也很麻烦&#xff0c;所以建议配置ssh密钥&#xff…

执行 java -jar xxx.jar 的时候底层到底做了什么?

大家都知道我们常用的 SpringBoot 项目最终在线上运行的时候都是通过启动 java -jar xxx.jar 命令来运行的。那你有没有想过一个问题&#xff0c;那就是当我们执行 java -jar 命令后&#xff0c;到底底层做了什么就启动了我们的 SpringBoot 应用呢&#xff1f;或者说一个 Sprin…

Redis删除了大量数据后,为什么内存占用还是很高?

前言 上周刚来了个应届小师弟&#xff0c;组长说让我带着&#xff0c;周二问了我这样一个问题&#xff1a;师兄啊&#xff0c;我用top命令看了下服务器的内存占用情况&#xff0c;发现Redis内存占用严重&#xff0c;于是我就删除了大部分不用的keys&#xff0c;为什么内存占用…

文件操作【C语言】

目录 一、为什么使用文件 二、什么是文件 1、程序文件 2、数据文件 3、文件名 三、文件的打开和关闭 1、文件指针 2、文件的打开和关闭 四、文件的顺序读写 五、文件的随机读写 1、fseek 2、ftell 3、rewind 七、文件读取结束的判定 1、被错误使用的feof 1、文…

unocss原子化

文章目录1. 安装2. 配置3. Unocss预设3.1 presetUno3.2 presetAttributify3.3 presetIcons了解什么是UnoCSS请看&#xff1a;重新构想原子化CSS - 知乎 github地址&#xff1a;UnoCSS UnoCSS搜索引擎 1. 安装 npm i -D unocss2. 配置 vite.config.ts import { defineConf…

分享微信抽奖小程序制作步骤_微信抽奖小程序怎么开发

各位商家在节日期间做活动的时候&#xff0c;都希望用更少的费用去或者更好的宣传和推广的效果。比较常见的就是抽奖活动小程序。无须玩家下载&#xff0c;通过微信扫码或者指定入口就可以参与。方便&#xff0c;效果又好。那么,性价比高的抽奖活动小程序怎么做&#xff1f; 来…

LabVIEW使用VI脚本重新排列对象

LabVIEW使用VI脚本重新排列对象VI脚本可用来重新排列前面板和程序框图的对象。该教程以程序框图对象重新排列为例。按照下列步骤&#xff0c;使用VI脚本重新排列程序框图对象。创建VI前&#xff0c;需先了解VI脚本的基本内容。必须启用VI脚本&#xff0c;才能显示VI脚本选板&am…

solr-cloud集群

Zookeeper集群搭建完成&#xff0c;下面开始构建solr-cloud从复制四个tomcat实例开始将配置好的单机版solr复制到tomcat实例下修改tomcat端口号vim tomcat01/conf/server.xmlvim tomcat02 /conf/server.xml使用配置好的单机版solrhome关联solr和solrhomevim tomcat01/webapps/s…

数据库系统概念 | 第三章:SQL介绍

文章目录&#x1f4da;SQL语言概览&#x1f4da;SQL数据定义&#x1f407;基本数据类型&#x1f407;基本模式定义&#x1f955;create table&#x1f955;create domain&#x1f955;drop table&#x1f955;delete table&#x1f955;alter table&#x1f4da;SQL查询的基本结…

Transformer模型详解

1. 前言 transformer结构是google在2017年的Attention Is All You Need论文中提出&#xff0c;在NLP的多个任务上取得了非常好的效果&#xff0c;可以说目前NLP发展都离不开transformer。最大特点是抛弃了传统的CNN和RNN&#xff0c;整个网络结构完全是由Attention机制组成。 …

VESC操作入门——控制霍尔电机、无感电机和AS5047P

目录一、设备说明二、VESC4驱动霍尔电机2.1、硬件准备2.2、硬件连接2.3、打开软件2.4、连接2.5、校准电机2.6、主界面操作三、VESC4驱动无感电机3.1、硬件准备3.2、硬件连接3.3、打开软件3.4、校准电机四、VESC4驱动AS5047P4.1、软硬件修改4.2、硬件准备4.3、硬件连接4.4、校准…

Win32解决透明字体改变时重叠的问题,GetClientRect与GetWindowRect的使用

透明字体,改变时发生文本重叠,解决办法是刷新窗体局部区域,该区域是文本或者按钮等控件的区域 Win32 API中使用InvalidateRect函数使指定区域失效,意味着要刷新该区域,再用UpdateWindow函数强迫窗体立即刷新 RECT rc; ... InvalidateRect(hWnd,&rc,true); UpdateWind…

Python操作文件及其内容的常用方式

Python操作文件及其内容的常用方式 文章目录Python操作文件及其内容的常用方式1&#xff1a;修改文件名1.1&#xff1a;修改指定文件名1.2&#xff1a;修改目录下的所有文件的文件名2&#xff1a;读取文件2.1&#xff1a;读取文件内容2.1.1&#xff1a;按行读取2.1.2&#xff1…

[Arduino]环境安装与配置

最近着迷与Arduio&#xff0c;可以连接控制各种器件帮助人类降低负担&#xff0c;如室内外温度动态采集、声控灯、自动给绿植浇水等各种应用&#xff0c;感觉挺有意思&#xff1b;随着最近两年物联网的推广及“万物互联”的普及&#xff0c;个人觉得物联网还是有点花样的&#…

认证授权功能分析

1 模块需求分析 1.1 什么是认证授权 截至目前&#xff0c;项目已经完成了课程发布功能&#xff0c;课程发布后用户通过在线学习页面点播视频进行学习。如何去记录学生的学习过程呢&#xff1f;要想掌握学生的学习情况就需要知道用户的身份信息&#xff0c;记录哪个用户在什么…

1949-2020年各省全要素生产率(年度)

1949-2020年各省全要素生产率(年度) 1、时间&#xff1a;1949-2020年 2、计算说明&#xff1a;产出为实际GDP&#xff0c;投入要素为从业人员数、固定资产&#xff08;永续盘存法&#xff09; 3、范围&#xff1a;包括31省 4、指标说明&#xff1a; 全要素生产率&#xf…

TiCDC 源码阅读(三)TiCDC 集群工作过程解析

内容概要 TiCDC 是一款 TiDB 增量数据同步工具&#xff0c;通过拉取上游 TiKV 的数据变更日志&#xff0c;TiCDC 可以将数据解析为有序的行级变更数据输出到下游。 本文是 TiCDC 源码解读的第三篇&#xff0c;主要内容是讲述 TiCDC 集群的启动及基本工作过程&#xff0c;将从…

MSR 5660设备进行流量整形和带宽保证的实现案例

组网及说明现场客户使用MSR5660设备替换客户处原有华为的设备&#xff0c;之前华为的设备做了GTS流量整形和WFQ的带宽保证&#xff0c;需求见下面图片描述配置步骤一、针对G1/0/1口入方向进行本地优先级的映射&#xff1a;&#xff08;1&#xff09;对进入设备G1/0/1口的流量进…