本期系列文章的其他文章

JDK1.8 Collection知识点与代码分析–整体框架
JDK1.8 Collection知识点与代码分析–ArrayList&LinkedList
JDK1.8 Collection知识点与代码分析–HashMap
JDK1.8 Collection知识点与代码分析–LinkedHashMap
JDK1.8 Collection知识点与代码分析–HashSet&TreeSet

这篇文章接着打开TreeMap的源码进行分析. 首先TreeMap是util工具类中一个非常好用的有序映射, 自身是非线程安全的, 相比HashMap, 增加了对NavigableMap, sortedMap接口的实现, 增加了一系列基于键有序的api, 这里先列举几个比较常用的
sortedMap

  • firstKey(): 返回最小的key
  • lastKey(): 返回最大的key
  • subMap(from, to): 返回的是map的view, 左闭右开
  • headMap(toKey, inclusive): 返回比toKey严格小(inclusive为true时包括)的这部分map的view
  • tailMap(fromKey, inclusive): 返回比fromKey严格大(inclusive为true时包括)的这部分map的view

NavigableMap(继承SortedMap)

  • ceilingEntry(key)/ ceilingKey(key): 返回大于等于key参数的最小的元素, 如果没有这样的元素存在返回null
  • floorEntry(key)/floorKey(key): 返回小于等于key参数的最大元素, 如果没有这样的元素, 返回null
  • higherEntry/higherKey: 和ceiling类似, 只不过严格大
  • lowerEntry/lowerKey: 和floor类似, 只不过严格小

TreeMap的核心数据结构是红黑树, 其红黑树的实现和HashMap中的红黑树实现几乎是一模一样的, 只不过函数名和获取父节点, 左右节点的方法有微小的差别, 以下贴出核心部分的代码.
fixAfterInsertion这个方法和HashMap中的balanceInsertion是一模一样的, 下面的注释看完没有弄懂的可以看下这篇讲hashMap的文章中的balanceInsertion部分, 我在注释中通过图解的方式, 对每个部分解决的问题进行了梳理.

private void fixAfterInsertion(Entry<K,V> x) {x.color = RED;while (x != null && x != root && x.parent.color == RED) {// x不是root节点, x的父节点为红节点// x的父节点是祖先节点的左儿子if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {Entry<K,V> y = rightOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {// flip ColorsetColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == rightOf(parentOf(x))) {// x是右节点, x和父节点是红节点, 所以先左旋x = parentOf(x);rotateLeft(x);}// x, 父节点是左儿子, 也都是红节点, 右旋setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateRight(parentOf(parentOf(x)));}} else {//父节点是右儿子Entry<K,V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {//flipsetColor(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;
}

TreeMap中一个需要注意的细节, 当它没有指定comparator时, 是不允许null作为key的, 使用comparator时, 取决于comparator是否允许key为null.

对于NavigatableMap的api中的方法, 这里只给出一个例子, 它们的思路都非常相似

final Entry<K,V> getHigherEntry(K key) {Entry<K,V> p = root;while (p != null) {int cmp = compare(key, p.key);if (cmp < 0) {// 如果当前节点比key大, 就继续往左找, 看是否能找到比当前更小的比key大的元素if (p.left != null)p = p.left;elsereturn p;} else {if (p.right != null) {p = p.right;} else {// 如果更大的没有了, 往父节点找, 找到第一个比key大的父节点(回溯,找到第一个向左走的位置)Entry<K,V> parent = p.parent;Entry<K,V> ch = p;while (parent != null && ch == parent.right) {ch = parent;parent = parent.parent;}return parent;}}}return null;
}

这个方法的返回值并不是直接返回的, 而是通过exportEntry方法, 构造了一个SimpleImmutableEntry的实例进行发布, 保证安全性.

而对于SortedMap的api中方法, 它们返回的都是TreeMap的静态内部类AscendingSubMap, 这个静态内部类又是继承于TreeMap的抽象静态内部类NavigableSubMap, 这个类的成员变量如下:


/*** The backing map.*/final TreeMap<K,V> m;/*** Endpoints are represented as triples (fromStart, lo,* loInclusive) and (toEnd, hi, hiInclusive). If fromStart is* true, then the low (absolute) bound is the start of the* backing map, and the other values are ignored. Otherwise,* if loInclusive is true, lo is the inclusive bound, else lo* is the exclusive bound. Similarly for the upper bound.*/final K lo, hi;final boolean fromStart, toEnd;final boolean loInclusive, hiInclusive;

可以看到, 它是成员TreeMap的一个view, 内部维护了lo,hi两个key, 它的ceiling, floor等方法, 都是在检查是否在lo, hi范围后, 调用TreeMapgetCeilingEntry等方法.

JDK1.8 Collection知识点与代码分析--TreeMap相关推荐

  1. 2017.4.18 静态代码分析工具sonarqube+sonar-runner的安装配置及使用

    配置成功后的代码分析页面: 可以看到对复杂度.语法使用.重复度等等都做了分析,具体到了每一个方法和每一句代码. 四种使用方式: sonarqube + sonar-runner sonarqube + ...

  2. TensorFlow-CIFAR10 CNN代码分析

    CIFAR 代码组织 代码分析 cifar10_trainpy cifar10py cifar10_evalpy Reference 根据TensorFlow 1.2.1,改了官方版本的报错. CIF ...

  3. 20145217《网络对抗》 恶意代码分析

    20145217<网络对抗> 免杀原理与实践 知识点学习总结 进行恶意代码分析之前必须具备以下知识:编程.汇编/反汇编.网络基本知识.PE文件结构以及一些常用行为分析软件. 一.在一个已经 ...

  4. Java 线程池框架核心代码分析

    转载自 Java 线程池框架核心代码分析 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executo ...

  5. c++代码健壮性_复活Navex-使用图查询进行代码分析(上)

    从了解到修复 Navex, 其中花了一年多, 从对自动化代码审计一无所知到学习PL/Static Analysis, 翻阅十几年前的文档, 补全Gremlin Step, 理解AST, CFG, DD ...

  6. beego 例子_beego框架代码分析

    前言 也许beego框架在国内应该是众多PHPer转go的首选,因为beego的MVC.ORM.完善的中文文档让PHPer们得心应手,毫无疑问我也是.这种感觉就像当年入门PHP时使用ThinkPHP一 ...

  7. 【直播回顾】云栖社区特邀专家关键:Java无锁集合代码分析

    主讲人:关键(云栖社区特邀专家) 目前在广州某家知名公司任职架构师,主要负责Spring Cloud.Dubbo.MQ.Zookeeper的规划. 平时比较热衷于研究互联网技术,热爱生活,希望能结交到 ...

  8. SRS 代码分析【mpeg-ts解析】

    SRS 代码分析[mpeg-ts解析] 1.SrsTsContext的decode接口定义如下: int SrsTsContext::decode(SrsBuffer* stream, ISrsTsH ...

  9. 你不知道的常用 代码分析 规范

    visual studio有个功能,代码分析,一般开发完毕后,除了处理常规的"错误列表"显示的"错误"和"警告",我们更加应该注意的是,运行 ...

  10. ARM GICv3 ITS介绍及代码分析

    前言: 在ARM gicv3中断控制器,有提到过ITS的作用,本篇就ITS进行更详细的介绍以及分析linux 内核中ITS代码的实现. 本文基于linux 4.19,介绍DT方式初始化的ITS代码. ...

最新文章

  1. mysql join not in_MySQL 使用左连接替换not in
  2. azkaban安装编译3.86 教程
  3. AtomicLong和LongAdder的区别
  4. centos rpm mysql 5.6_centos6.5 mysql5.6 RPM安装
  5. docker安装执行问题
  6. 蓝桥杯 AGLO-152 算法训练 8-2求完数
  7. Python 数据结构与算法 —— 从分治的角度看快速排序、归并排序
  8. UltraEdit(UE)如何设置去掉.bak备份文件?
  9. 通过android手机内置GPS获取平面直角坐标和高斯坐标的原理(附代码)
  10. Android 代码名字-API级别-版本号-NDK版本对应关系
  11. 【数学建模】CUMCM-2017A CT系统参数标定及成像 思路及部分代码
  12. DirectShow笔记
  13. 5分钟学会制作自动化脚本——自动化脚本辅助开发IDE——Selenium IDE介绍(测试工程师必备)
  14. 2160亿元电费如何降下来?地方5G“硬核”政策助力
  15. Git配置详细教程及基础使用方法,教你轻松学会git代码管理
  16. php图片的编码是什么,jpeg是什么图像压缩编码标准
  17. 2022联想小新pro14和联想小新pro16 区别 哪个好
  18. Informix 12.10版本新特性-2
  19. 超分辨率(CVPR2020) ~《Video Super-resolution with Temporal Group Attention》
  20. Interproscan linux版本详细安装教程及运行报错解决方案

热门文章

  1. 一文揭晓:大数据是什么?大数据如何分类?又该怎么学?
  2. ERROR ITMS-4238
  3. Android Studio实现多媒体播放器,音乐视频一体化
  4. 金蝶服务器系统用什么,金蝶用哪个云服务器
  5. c语言汉诺塔课设计报告,汉诺塔游戏的设计
  6. ELF文件格式, ELF文件是什么,里面包含什么内容
  7. 烽火2640路由器命令行手册-03-广域网配置命令
  8. 安卓集成facebook_设计和编码集成的Facebook应用程序:理论
  9. 数据库服务器使用的RAID存储架构初步介绍
  10. 猿辅导python大纲_解读独角兽企业“猿辅导”(一)