吃饭间隙,迷上了《吐槽大会》,一集一集地刷啊,觉得这些嘉宾真的挺有勇气的,敢于直面自己的惨淡槽点。于是,同学们看到了,我作为一个技术博主,也受到了“传染”,不,受到了“熏陶”,本来这篇文章标题就想叫《TreeMap 指南》,是不是有点平淡无奇,没有槽点?于是我就想,不妨蹭点吐槽大会的热度吧,虽然吐槽大会现在也没什么热度了哈。

TreeMap,虽然也是个 Map,但存在感太低了。我做程序员这十多年里,HashMap 用了超过十年,TreeMap 只用了多字里那么一小会儿一小会儿,真的是,太惨了。

虽然 TreeMap 用得少,但还是有用处的。

之前 LinkedHashMap那篇文章里提到过了,HashMap 是无序的,所有有了 LinkedHashMap,加上了双向链表后,就可以保持元素的插入顺序和访问顺序,那 TreeMap 呢?

TreeMap 由红黑树实现,可以保持元素的自然顺序,或者实现了 Comparator 接口的自定义顺序。

可能有些同学不知道红黑树,理解起来 TreeMap 就有点难度,那我先来普及一下:

红黑树(英语:Red–black tree)是一种自平衡的二叉查找树(Binary Search Tree),结构复杂,但却有着良好的性能,完成查找、插入和删除的时间复杂度均为 log(n)。

二叉查找树又是什么呢?

image

上图中这棵树,就是一颗典型的二叉查找树:

1)左子树上所有节点的值均小于或等于它的根结点的值。

2)右子树上所有节点的值均大于或等于它的根结点的值。

3)左、右子树也分别为二叉排序树。

理解二叉查找树了吧?不过,二叉查找树有一个不足,就是容易变成瘸子,就是一侧多,一侧少,就像下图这样:

查找的效率就要从 log(n) 变成 o(n) 了,对吧?必须要平衡一下,对吧?于是就有了平衡二叉树,左右两个子树的高度差的绝对值不超过 1,就像下图这样:

红黑树,顾名思义,就是节点是红色或者黑色的平衡二叉树,它通过颜色的约束来维持着二叉树的平衡:

1)每个节点都只能是红色或者黑色

2)根节点是黑色

3)每个叶节点(NIL 节点,空节点)是黑色的。

4)如果一个节点是红色的,则它两个子节点都是黑色的。也就是说在一条路径上不能出现相邻的两个红色节点。

5)从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

那,关于红黑树,同学们就先了解到这,脑子里有个大概的印象,知道 TreeMap 是个什么玩意。

01、自然顺序

默认情况下,TreeMap 是根据 key 的自然顺序排列的。比如说整数,就是升序,1、2、3、4、5。

TreeMap mapInt = new TreeMap<>();mapInt.put(3, "11");mapInt.put(2, "12");mapInt.put(1, "13");mapInt.put(5, "14");mapInt.put(4, "15");System.out.println(mapInt);

输出结果如下所示:

{1=11, 2=12, 3=13, 4=14, 5=15}

TreeMap 是怎么做到的呢?想一探究竟,就得上源码了,来看 TreeMap 的 put() 方法(省去了一部分,版本为 JDK 14):

public V put(K key, V value) {    TreeMap.Entry t = root;    int cmp;    TreeMap.Entry parent;    // split comparator and comparable paths    Comparator super K> cpr = comparator;    if (cpr != null) {    }    else {        @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                return t.setValue(value);        } while (t != null);    }    return null;}

注意 cmp = k.compareTo(t.key) 这行代码,就是用来进行 key 的比较的,由于此时 key 是 int,所以就会调用 Integer 类的 compareTo() 方法进行比较。

public int compareTo(Integer anotherInteger) {    return compare(this.value, anotherInteger.value);}public static int compare(int x, int y) {    return (x < y) ? -1 : ((x == y) ? 0 : 1);}

那相应的,如果 key 是字符串的话,也就会调用 String 类的 compareTo() 方法进行比较。

public int compareTo(String anotherString) {    byte v1[] = value;    byte v2[] = anotherString.value;    byte coder = coder();    if (coder == anotherString.coder()) {        return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)                : StringUTF16.compareTo(v1, v2);    }    return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)            : StringUTF16.compareToLatin1(v1, v2);}

由于内部是由字符串的字节数组的字符进行比较的,是不是听起来很绕?对,就是很绕,所以使用中文字符串作为 key 的话,看不出来效果。

TreeMap mapString = new TreeMap<>();mapString.put("c", "沉默王二");mapString.put("b", "沉默王二");mapString.put("a", "沉默王二");mapString.put("e", "沉默王二");mapString.put("d", "沉默王二");System.out.println(mapString);

输出结果如下所示:

{a=沉默王二, b=沉默王二, c=沉默王二, d=沉默王二, e=沉默王二}

字母的升序,对吧?

02、自定义排序

如果自然顺序不满足,那就可以在声明 TreeMap 对象的时候指定排序规则。

TreeMap mapIntReverse = new TreeMap<>(Comparator.reverseOrder());mapIntReverse.put(3, "沉默王二");mapIntReverse.put(2, "沉默王二");mapIntReverse.put(1, "沉默王二");mapIntReverse.put(5, "沉默王二");mapIntReverse.put(4, "沉默王二");System.out.println(mapIntReverse);

TreeMap 提供了可以指定排序规则的构造方法:

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

Comparator.reverseOrder() 返回的是 ReverseComparator 对象,就是用来反转顺序的,非常方便。

所以,输出结果如下所示:

{5=沉默王二, 4=沉默王二, 3=沉默王二, 2=沉默王二, 1=沉默王二}

HashMap 是无序的,插入的顺序随着元素的增加会不停地变动。但 TreeMap 能够至始至终按照指定的顺序排列,这对于需要自定义排序的场景,实在是太有用了!

03、排序的好处

既然 TreeMap 的元素是经过排序的,那找出最大的那个,最小的那个,或者找出所有大于或者小于某个值的键来说,就方便多了。

Integer highestKey = mapInt.lastKey();Integer lowestKey = mapInt.firstKey();Set keysLessThan3 = mapInt.headMap(3).keySet();Set keysGreaterThanEqTo3 = mapInt.tailMap(3).keySet();System.out.println(highestKey);System.out.println(lowestKey);System.out.println(keysLessThan3);System.out.println(keysGreaterThanEqTo3);

TreeMap 考虑得很周全,恰好就提供了 lastKey() 、 firstKey() 这样获取最后一个 key 和第一个 key 的方法。

headMap() 获取的是到指定 key 之前的 key; tailMap() 获取的是指定 key 之后的 key(包括指定 key)。

来看一下输出结果:

51[1, 2][3, 4, 5]

04、如何选择 Map

在学习 TreeMap 之前,我们已经学习了 [HashMap] 和 [LinkedHashMap],那如何从它们三个中间选择呢?

HashMap、LinkedHashMap、TreeMap 都实现了 Map 接口,并提供了几乎相同的功能(增删改查)。它们之间最大的区别就在于元素的顺序:

HashMap 完全不保证元素的顺序,添加了新的元素,之前的顺序可能完全逆转。

LinkedHashMap 默认会保持元素的插入顺序。

TreeMap 默认会保持 key 的自然顺序(根据 compareTo() 方法)。

来个表格吧,一目了然。

谢谢大家,下期见,同学们。

注:如果文章有任何问题,欢迎毫不留情地指正。
转自:沉默王二原文 http://www.itwanger.com/java/2020/08/24/java-treemap.html

java表格树_Java程序员值得拥有的TreeMap指南相关推荐

  1. 高级Java程序员值得拥有的10本书

    本文是码农网原创翻译,转载请看清文末的转载要求,谢谢合作! Java是时下最流行的编程语言之一.市面上也出现了适合初学者的大量书籍.但是对于那些在Java编程上淫浸多时的开发人员而言,这些书的内容未免 ...

  2. java的六大_java程序员必备的六大工具!

    原标题:java程序员必备的六大工具! Java程序员都会有套工具来应对工作上的挑战.多年来,Java 程序员使用软件来完成他们的工作.有很多工具对他们是有用的,不过对于初入行的人员来说,寻找合适的工 ...

  3. java cron表达式_Java 程序员都应该去使用一下这款强大的国产工具类库

    大家好,我是你们的导师, 我会坚持每天给大家整理一些干货内容(当然周末也要允许老师休息一下哈). 今天给大家分享一个很棒的Java工具类库:Hutool.可能有很多朋友已经知道这个类库了,甚至在已经在 ...

  4. java自动封箱_Java程序员面试,自动封箱/拆箱原理与包装类的缓冲机制你知道么?(转)...

    概述 本文中小编为大家细致的讲解了Java中基本数据类型对应的包装类以及包装类的缓冲机制在实际开发中的应用 . 并且对Java中基本数据类型的包装类的主要应用---自动封箱.自动拆箱做了底层剖析 . ...

  5. java 排名相同_Java程序员十年面试经验,助你成为offer收割机

    关于面试 其实关于面试总结这块,小编也看过不少文章,但是大部分都是关于面试题以及面试中的问题所谈.但是对于一个不擅长找工作的人来说,其实在简历制作这一块很重要.因为简历没写好的话,连面试邀请都没,何谈 ...

  6. java tostring方法_Java程序员小伙启动项目报错,原来是使用了lombok

    每一个程序员在进公司的第一天,可能是在搭建环境,启动项目.小伙在启动 SpringBoot 项目中发现:代码中缺失大量的 getter/setter ,一查原来使用 lombok . 首先,解决项目中 ...

  7. java 招聘要求_Java程序员如何进阶,一般招聘都有哪些要求?

    作为当前市场上应用领域最广.人才需求最大的编程语言,Java一直是人们入行IT行业的选择.然而,随着大批量的人涌入Java开发行业,企业的招聘门槛也在无形中抬高,提升专业技能成为新手Java程序员实现 ...

  8. erp开发和java开发区别_Java程序员求职必学:Spring boot学习指南!

    黑马程序员上海中心 学姐微信:CZBKSH 关注 咳咳,今天学姐就来和你们说说Spring对于Java程序员的重要性. 首先,Spring 官网首页是这么介绍自己的--"Spring: th ...

  9. java p8级别_JAVA程序员月入5000+很迷茫,如何能在一年内改变达到月入过万?

    生活中有那样一群人,他们总能是很聪明有主见,知道自己在什么时候该做什么.在什么时候该选什么,我们也能这样该多好呀!不用羡慕他们,看完本文你就能知道. 正文: 从标题来看,主线不是"如何能在一 ...

最新文章

  1. 36氪研究 | 智慧零售行业研究报告
  2. 基于SSM实现个人健康管理系统
  3. 两台oracle怎样定期导表数据,定期从Oracle导数据至MySQL
  4. kudu大量数据更新_Apache Kudu又更新?1.4版改进了Web界面
  5. 2修改字段名_DevExpress ASP.NET v18.2新功能详解(二)
  6. 基于AI的超分辨技术在RTC领域的技术难点与挑战
  7. 数据仓库、商业智能的体系结构
  8. 第一冲刺阶段博客检查
  9. python流量监控_用python中的pcapy实现网络流量监控
  10. java运行字符串代码
  11. 3项目里面全局用less变量 cli vue_vue-cli3全局载入scss变量或less变量配置
  12. Himly TCC Dubbo 程序示例
  13. 工厂模式在 Calendar 类中的应用
  14. 单片机C语言程序设计实训100例大集合
  15. 大班科学电子计算机,计算器教案
  16. mac电脑重复文件如何查找?
  17. KM小鼠大脑海马区生理切片HE染色
  18. 计算机死机启动方法,电脑开机死机的解决方法
  19. C++实现OPT最佳页面替换算法,结果简明扼要
  20. 保健用品行业智慧供应链管理系统:高度整合产业链资源,精细化企业供应商管理

热门文章

  1. 【机器学习】线性回归之Normal Equation(矩阵求导与线性代数视角)
  2. matlab里sconv原理_第6章 信号的时域分析及Matlab实现.ppt
  3. JEPaas【按钮隐藏】根据单据状态值决定【添加明细】【删除】按钮的隐藏和显示
  4. 为myeclipse分配更大的内存
  5. 革命性新特性 | 单一应用跨多Kubernetes集群的部署与管理
  6. 用 Nginx 基于 Let's Engypt 免费证书打造快速安全的 HTTPS 网站
  7. angularjs 弹出框 $modal传值
  8. DHCP服务_学习笔记
  9. Radio / Select 设置 checked 没反应
  10. IOS 手机助手及越狱助手推荐