JDK1.8 Collection知识点与代码分析--TreeMap
本期系列文章的其他文章
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()
: 返回最小的keylastKey()
: 返回最大的keysubMap(from, to)
: 返回的是map的view, 左闭右开headMap(toKey, inclusive)
: 返回比toKey严格小(inclusive为true时包括)的这部分map的viewtailMap(fromKey, inclusive)
: 返回比fromKey严格大(inclusive为true时包括)的这部分map的view
NavigableMap(继承SortedMap)
ceilingEntry(key)/ ceilingKey(key)
: 返回大于等于key参数的最小的元素, 如果没有这样的元素存在返回nullfloorEntry(key)/floorKey(key)
: 返回小于等于key参数的最大元素, 如果没有这样的元素, 返回nullhigherEntry/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范围后, 调用TreeMap
的getCeilingEntry
等方法.
JDK1.8 Collection知识点与代码分析--TreeMap相关推荐
- 2017.4.18 静态代码分析工具sonarqube+sonar-runner的安装配置及使用
配置成功后的代码分析页面: 可以看到对复杂度.语法使用.重复度等等都做了分析,具体到了每一个方法和每一句代码. 四种使用方式: sonarqube + sonar-runner sonarqube + ...
- TensorFlow-CIFAR10 CNN代码分析
CIFAR 代码组织 代码分析 cifar10_trainpy cifar10py cifar10_evalpy Reference 根据TensorFlow 1.2.1,改了官方版本的报错. CIF ...
- 20145217《网络对抗》 恶意代码分析
20145217<网络对抗> 免杀原理与实践 知识点学习总结 进行恶意代码分析之前必须具备以下知识:编程.汇编/反汇编.网络基本知识.PE文件结构以及一些常用行为分析软件. 一.在一个已经 ...
- Java 线程池框架核心代码分析
转载自 Java 线程池框架核心代码分析 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executo ...
- c++代码健壮性_复活Navex-使用图查询进行代码分析(上)
从了解到修复 Navex, 其中花了一年多, 从对自动化代码审计一无所知到学习PL/Static Analysis, 翻阅十几年前的文档, 补全Gremlin Step, 理解AST, CFG, DD ...
- beego 例子_beego框架代码分析
前言 也许beego框架在国内应该是众多PHPer转go的首选,因为beego的MVC.ORM.完善的中文文档让PHPer们得心应手,毫无疑问我也是.这种感觉就像当年入门PHP时使用ThinkPHP一 ...
- 【直播回顾】云栖社区特邀专家关键:Java无锁集合代码分析
主讲人:关键(云栖社区特邀专家) 目前在广州某家知名公司任职架构师,主要负责Spring Cloud.Dubbo.MQ.Zookeeper的规划. 平时比较热衷于研究互联网技术,热爱生活,希望能结交到 ...
- SRS 代码分析【mpeg-ts解析】
SRS 代码分析[mpeg-ts解析] 1.SrsTsContext的decode接口定义如下: int SrsTsContext::decode(SrsBuffer* stream, ISrsTsH ...
- 你不知道的常用 代码分析 规范
visual studio有个功能,代码分析,一般开发完毕后,除了处理常规的"错误列表"显示的"错误"和"警告",我们更加应该注意的是,运行 ...
- ARM GICv3 ITS介绍及代码分析
前言: 在ARM gicv3中断控制器,有提到过ITS的作用,本篇就ITS进行更详细的介绍以及分析linux 内核中ITS代码的实现. 本文基于linux 4.19,介绍DT方式初始化的ITS代码. ...
最新文章
- mysql join not in_MySQL 使用左连接替换not in
- azkaban安装编译3.86 教程
- AtomicLong和LongAdder的区别
- centos rpm mysql 5.6_centos6.5 mysql5.6 RPM安装
- docker安装执行问题
- 蓝桥杯 AGLO-152 算法训练 8-2求完数
- Python 数据结构与算法 —— 从分治的角度看快速排序、归并排序
- UltraEdit(UE)如何设置去掉.bak备份文件?
- 通过android手机内置GPS获取平面直角坐标和高斯坐标的原理(附代码)
- Android 代码名字-API级别-版本号-NDK版本对应关系
- 【数学建模】CUMCM-2017A CT系统参数标定及成像 思路及部分代码
- DirectShow笔记
- 5分钟学会制作自动化脚本——自动化脚本辅助开发IDE——Selenium IDE介绍(测试工程师必备)
- 2160亿元电费如何降下来?地方5G“硬核”政策助力
- Git配置详细教程及基础使用方法,教你轻松学会git代码管理
- php图片的编码是什么,jpeg是什么图像压缩编码标准
- 2022联想小新pro14和联想小新pro16 区别 哪个好
- Informix 12.10版本新特性-2
- 超分辨率(CVPR2020) ~《Video Super-resolution with Temporal Group Attention》
- Interproscan linux版本详细安装教程及运行报错解决方案
热门文章
- 一文揭晓:大数据是什么?大数据如何分类?又该怎么学?
- ERROR ITMS-4238
- Android Studio实现多媒体播放器,音乐视频一体化
- 金蝶服务器系统用什么,金蝶用哪个云服务器
- c语言汉诺塔课设计报告,汉诺塔游戏的设计
- ELF文件格式, ELF文件是什么,里面包含什么内容
- 烽火2640路由器命令行手册-03-广域网配置命令
- 安卓集成facebook_设计和编码集成的Facebook应用程序:理论
- 数据库服务器使用的RAID存储架构初步介绍
- 猿辅导python大纲_解读独角兽企业“猿辅导”(一)