TreeMap源码详解

news2024/11/16 16:50:44

优质博文:IT-BLOG-CN

背景:昨天有人问我,他想将Map中的Key按照顺序进行遍历,我说直接使用keySet方法获取到Set集合,因为它是集成Collection接口,所以包含了sort方法后遍历取value值即可。但当看到TreeMap的那一刻,我发现自己错了。

再说一个很重要的概念:
【1】TreeMapkey不能为nullvalue可以为null;
【2】HashMapkey可以为nullvalue可以为null;
【3】ConcurrentHashMapHashTablekeyvalue都不可以为null;

一、TreeMap 结构

如类图所示:TreeMap也是一种Map实现了SortedMap<K,V>接口,说明他内部对Key进行了排序。而HashMap内部的Key是无序的。TreeMap的底层是一颗红黑树,这是一种自然平衡二叉搜索树,可以保证在插入,删除,查找等操作中的时间复杂度为O(log n)

使用TreeMap时,放入的Key必须实现Comparable接口。StringInteger这些类已经实现了Comparable接口,因此可以直接作为Key使用。作为Value的对象则没有任何要求。

如果作为Keyclass没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法,我们可以先看下TreeMap的构造器:

// 默认构造函数。使用该构造函数,TreeMap中的元素按照自然排序进行排列。
TreeMap()
 
// 创建的TreeMap包含Map
TreeMap(Map<? extends K, ? extends V> copyFrom)
 
// 指定Tree的比较器
TreeMap(Comparator<? super K> comparator)
 
// 创建的TreeSet包含copyFrom
TreeMap(SortedMap<K, ? extends V> copyFrom)

实际开发中案例:Comparator接口要求实现一个比较方法,它负责比较传入的两个元素p1p2,如果p1 < p2,则返回负数,通常是-1,如果p1 == p2,则返回0,如果p1 > p2,则返回正数,通常是1TreeMap内部根据比较结果对Key进行排序。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
            public int compare(Person p1, Person p2) {
                return p1.name.compareTo(p2.name);
            }
        });
        map.put(new Person("Tim"), 1);
        map.put(new Person("Baby"), 2);
        map.put(new Person("Luna"), 3);
        for (Person key : map.keySet()) {
            System.out.println(key);
        }
        // {Person: Baby}, {Person: Luna}, {Person: Tim}
        System.out.println(map.get(new Person("Baby"))); // 2
    }
}

class Person {
    public String name;
    Person(String name) {
        this.name = name;
    }
    public String toString() {
        return "{Person: " + name + "}";
    }
}

Person类并未覆写equals()hashCode(),因为TreeMap不使用equals()hashCode()

二、TreeMap API

基础数据:

// 创建一个 TreeMap
TreeMap<String, Integer> map = new TreeMap<>();

// 向 TreeMap 中添加键值对
map.put("Alice", 90);
map.put("Bob", 80);
map.put("Charlie", 70);
map.put("David", 60);

TreeMap因为排序等特性,所以包含了一些特殊的API这里简单介绍一下:

【1】firstKeylastKey:分别返回TreeMap中最小和最大的键;

// 返回 TreeMap 中最小和最大的键
System.out.println(map.firstKey()); // Alice
System.out.println(map.lastKey()); // David

【2】lowerKeyhigherKey:分别返回TreeMap中小于和大于给定键的最近的键;

// 返回 TreeMap 中小于和大于给定键的最近的键
System.out.println(map.lowerKey("Bob")); // Alice
System.out.println(map.higherKey("Bob")); // Charlie

【3】floorKeyceilingKey:分别返回TreeMap中小于等于和大于等于给定键的最近的键;

// 返回 TreeMap 中小于等于和大于等于给定键的最近的键
System.out.println(map.floorKey("Bob")); // Bob
System.out.println(map.ceilingKey("Bob")); // Bob

【4】subMap:返回一个子映射,包含给定范围内的所有键值对;

// 返回一个子映射,包含给定范围内的所有键值对
System.out.println(map.subMap("Alice", true, "Charlie", true)); // {Alice=90, Bob=80, Charlie=70}

【5】headMaptailMap:分别返回一个子映射,包含小于或等于给定键或大于或等于给定键的所有键值对;

// 返回一个子映射,包含小于或等于给定键或大于或等于给定键的所有键值对
System.out.println(map.headMap("Charlie", true)); // {Alice=90, Bob=80, Charlie=70}
System.out.println(map.tailMap("Charlie", true)); // {Charlie=70, David=60}

【6】firstEntrylastEntry:分别返回TreeMap中最小和最大的键值对;

// 返回 TreeMap 中最小和最大的键值对
System.out.println(map.firstEntry()); // Alice=90
System.out.println(map.lastEntry()); // David=60

【7】lowerEntryhigherEntry:分别返回TreeMap中小于和大于给定键的最近的键值对;

// 返回 TreeMap 中小于和大于给定键的最近的键值对
System.out.println(map.lowerEntry("Bob")); // Alice=90
System.out.println(map.higherEntry("Bob")); // Charlie=70

【8】floorEntryceilingEntry:分别返回TreeMap中小于等于和大于等于给定键的最近的键值对;

// 返回 TreeMap 中小于等于和大于等于给定键的最近的键值对
System.out.println(map.floorEntry("Bob")); // Bob=80
System.out.println(map.ceilingEntry("Bob")); // Bob=80

【9】pollFirstEntrypollLastEntry:分别返回并删除TreeMap中最小和最大的键值对;

// 返回并删除 TreeMap 中最小和最大的键值对
System.out.println(map.pollFirstEntry()); // Alice=90
System.out.println(map.pollLastEntry()); // David=60

根据上述的API可知,如果遇到如下场景时,应当想到使用TreeMap
【1】需要按照键的自然顺序或者指定的比较器进行排序的场景,例如字典,排行榜,日程安排等。
【2】需要快速找到映射中最小或者最大的键或者值的场景,例如优先队列,堆,区间查询等。
【3】需要快速找到映射中某个范围内的所有键或者值的场景,例如范围搜索,前缀匹配,区间统计等。

三、注意事项

【1】如果使用自然顺序进行排序,则需要保证键是可比较的(实现了Comparable接口),否则会抛出ClassCastException异常。
【2】如果使用指定的比较器进行排序,则需要保证比较器是一致的(满足自反性,对称性,传递性),否则可能导致排序结果不正确或者抛出ClassCastException异常。
【3】如果在遍历TreeMap的过程中修改了其结构(添加或删除了元素),则可能导致ConcurrentModificationException异常或者不确定的行为。
【4】如果在遍历TreeMap的过程中修改了其元素(改变了键或者值),则可能导致排序结果不正确或者不确定的行为。
【5】TreeMap不是线程安全的,如果多个线程同时访问或修改TreeMap,则需要进行同步处理。

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

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

相关文章

差旅报销的数智化转型 以分贝通为例

企业差旅报销的数智化转型之所以势在必行&#xff0c;源于传统差旅报销方式在效率、合规性和成本控制等方面存在严重不足。作为服务企业的一体化差旅报销管理平台&#xff0c;分贝通结合数千家合作伙伴的实际案例为企业提供定制化的差旅报销数智化解决方案&#xff0c;帮助企业…

【Python报错已解决】AttributeError: ‘tuple‘ object has no attribute ‘log_softmax‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

CentOS 7 中安装 docker 环境

作者&#xff1a;程序那点事儿 日期&#xff1a;2023/02/15 02:31 官网地址 官网文档 docker三种网络模式 Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0c; CentOS 7 满足最低内核的要求。 Docker 分为 CE 和 EE 两大版本。CE 即社区…

前端开发必备:实用Tool封装工具类方法大全

程序员必备宝典网站https://tmxkj.top/#/ 1.判断空值 /*** 判断是否是空值*/ export function isEmpty(value) {if (typeof value string) {return value.length 0 || value "";} else if (typeof value number) {return value 0;} else if (Array.isArray(va…

TypeScript 设计模式之【抽象工厂模式】

文章目录 抽象工厂模式:一个神奇的玩具制造商主要思想:玩具制造商的秘密这个制造商有什么好处和坏处?如何打造这个神奇的玩具制造商?代码实现案例抽象工厂模式主要优点抽象工厂模式主要缺点抽象工厂模式适用场景总结 抽象工厂模式:一个神奇的玩具制造商 想象一下&#xff0c…

(done) 声音信号处理基础知识(7) (Understanding Time Domain Audio Features)

参考&#xff1a;https://www.youtube.com/watch?vSRrQ_v-OOSg&t1s 时域特征包括&#xff1a; 1.幅度包络 2.均方根能量 3.过零率 振幅包络的定义&#xff1a;一个 frame 里&#xff0c;所有采样点中最大的振幅值 一个形象的关于振幅包络的可视化解释如下&#xff1a;…

基于SpringBoot + Vue的大学生日常消费管理系统设计与实现

文章目录 前言一、详细操作演示视频二、具体实现截图三、技术栈1.前端-Vue.js2.后端-SpringBoot3.数据库-MySQL4.系统架构-B/S 四、系统测试1.系统测试概述2.系统功能测试3.系统测试结论 五、项目代码参考六、数据库代码参考七、项目论文示例结语 前言 &#x1f49b;博主介绍&a…

fiddler抓包07_抓IOS手机请求

课程大纲 前提&#xff1a;电脑和手机连接同一个局域网 &#xff08;土小帽电脑和手机都连了自己的无线网“tuxiaomao”。&#xff09; 原理如下&#xff1a; 电脑浏览器抓包时&#xff0c;直接就是本机网络。手机想被电脑Fiddler抓包&#xff0c;就要把Fiddler变成手机和网络…

浅拷贝和深拷贝(Java 与 JavaScript)

一、Java 浅拷贝和深拷贝 在Java中&#xff0c;浅拷贝和深拷贝的主要区别在于对对象的引用和内容的复制方式。 浅拷贝 Java 的类型有基本数据类型和引用类型&#xff0c;基本数据类型是可以由 CPU 直接操作的类型&#xff0c;无论是深拷贝还是浅拷贝&#xff0c;都是会复制出…

ANSYS Workbench晶体结构Voronoi泰森多边形建模

在ANSYS Workbench内建立包含晶格及晶格边界在内的晶体结构模型&#xff0c;可用于模拟多种物理现象及材料行为。晶格模型适用于研究微观尺度下的材料性质&#xff0c;以及它们如何影响宏观性能&#xff0c;如进行金属晶体结构建模及断裂的模拟等。 晶体结构模型可采用CAD Vo…

fastapp-微信开发GPT项目第一课

0. 开发说明 在学习开发本项目之前&#xff0c;必须保证有以下知识储备和环境工具。 技术栈说明python>3.9、pydantic>2.7.1python基础&#xff0c;http协议fastapi>0.111.0web协程异步框架&#xff0c;有web开发基础&#xff0c;异步编程&#xff0c;类型标注[pyth…

银河麒麟操作系统中查看动态库函数的方法

银河麒麟操作系统中查看动态库函数的方法 1、查看单个动态库中的函数2、查找特定函数位于哪个动态库中 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Linux系统&#xff0c;包括银河麒麟操作系统中&#xff0c;动态库&#xff08;.so文件…

单片机项目合集列表与专栏说明——Excel合集列表目录查阅(持续更新)

阿齐Archie《单片机项目合集》专栏项目 为方便查找本专栏的项目&#xff0c;特整理Excel合集列表供查阅&#xff08;可搜索或按系列查找&#xff09; 持续更新链接如下&#xff1a; 阿齐单片机项目合集 (kdocs.cn)https://www.kdocs.cn/l/cmrxCxJN05YN 打开链接如下Exce表所…

【LeetCode:219. 存在重复元素 II + 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

人工智能领域-----机器学习和深度学习的区别

机器学习和深度学习都是人工智能领域中的重要概念&#xff0c;它们之间存在以下一些区别&#xff1a; 一、定义与概念 机器学习&#xff1a; 是一种让计算机自动学习和改进的方法&#xff0c;通过从数据中学习模式和规律&#xff0c;从而能够对新的数据进行预测或决策。涵盖了…

Android compose 的基本环境搭建

1.创建项目 导入版本 1.gradle/libs.versions.toml [versions] accompanistPermissions "0.36.0" agp "8.5.0-beta01" coilCompose "2.7.0" constraintlayoutComposeVersion "1.0.1" hiltAndroid "2.51.1" hiltNavi…

git其他人有改动,自己这边不要pull,先stash一下,pull了然后apply自己的stash。

git其他人有改动&#xff0c;自己这边不要pull&#xff0c;先stash一下&#xff0c;pull了然后apply自己的stash&#xff0c; git 冲突了怎么处理处理&#xff1f; git会提示冲突的文件 On branch experiments You have unmerged paths.(fix conflicts and run "git comm…

代码随想录 -- 回溯 -- 子集II

90. 子集 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 题目中说明nums中可能包含重复元素&#xff0c;所以要去重。 去重的前提是将数组nums排序&#xff01; 递归参数&#xff1a;nums&#xff0c;index&#xff0c;path递归出口&#xff1a;当遍历完num…

编译和链接笔记

翻译环境和运⾏环境 在ANSI C的任何⼀种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执⾏的机器指令&#xff08;⼆进制指令&#xff09;。 第2种是执⾏环境&#xff0c;它⽤于实际执⾏代码。 1.翻译环境 其实翻译环境…