【JDK1.8】JDK1.8集合源码阅读——TreeMap(一)
一、前言
在前面两篇随笔中,我们提到过,当HashMap的桶过大的时候,会自动将链表转化成红黑树结构,当时一笔带过,因为我们将留在本章中,针对TreeMap进行详细的了解。
二、TreeMap的继承关系
下面先让我们来看一下TreeMap的继承关系,对它有一个大致的了解:

可以看到,除了在之前HashMap里常见的继承类和接口以外,TreeMap实现了NavigableMap接口,而NavigableMap继承自SortedMap,由名字可以看出,只是一个用来实现排序的接口。而这也是为什么TreeMap能够实现排序的原因。由于篇幅关系,将TreeMap的源码解析分为三部分,本章将对接口NavigableMap以及SortedMap进行解析。
三、SortedMap接口源码解析
3.1 SortedMap接口
public interface SortedMap<K,V> extends Map<K,V> {
//返回用于对键的进行排序的比较器,如果此映射使用其键的自然排序,则为null
Comparator<? super K> comparator();
//返回从fromKey(包括)到toKey(不包括)之间的map
SortedMap<K,V> subMap(K fromKey, K toKey);
//返回小于toKey的map
SortedMap<K,V> headMap(K toKey);
//返回大于或等于fromKey的map
SortedMap<K,V> tailMap(K fromKey);
//返回map中第一个(最低)键
K firstKey();
//返回map中最后一个(最高)键
K lastKey();
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
}
SortedMap的接口比较简单,没有很特别的地方,唯一比较特别的就是返回Comparator这个接口,可以设想实现排序功能的秘密或许就藏在此处。下面让我们来看一下Comparator和Comparable接口,两者之间有点关联,可以理解为Comparable自带了比较功能,而Comparator是赋予没有比较能力的对象一种比较能力。举个简单例子:面对一道计算题,小明天生口算能力很强,看一眼就能算出来答案。而小李没有这种能力,需要借助计算器才能得出答案。
3.2 Comparable接口
先让我们看下它的代码:
public interface Comparable<T> {
//如果小于o,返回负数;等于o,返回0;大于o返回正数。
public int compareTo(T o);
}
对,就是这么简单,里面传入一个泛型T的对象o,对o进行比较。如果小于o,返回负数;等于o,返回0;大于o返回正数。
我们熟悉的很多对象如String,Integer,Double等都实现了这个接口。可以来看一下简单的例子:
public class Item implements Comparable<Item> {
private String name;
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public int getPrice() {
return price;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Item{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Item o) {
if (this.name.compareTo(o.name) < 0) {
return -1;
} else if (this.name.compareTo(o.name) > 0) {
return 1;
} else {
return 0;
}
}
public static void main(String[] args) {
List<Item> items = Arrays.asList(new Item("banana", 200), new Item("apple", 400));
System.out.println("before:" + items);
Collections.sort(items);
System.out.println("after:" + items);
}
}
上述main函数的输出:
before:[Item{name='banana', price=200}, Item{name='apple', price=400}]
after:[Item{name='apple', price=400}, Item{name='banana', price=200}]
上面的例子中,我们自己实现了Comparable接口,比较了Item的name属性,然后通过Collections.sort对它进行了排序(值得注意的是:没有实现Comparable接口的对象不能使用该方法)。但是,如果我不想用name属性对它进行排序,想对price进行排序呢,或者先对name排序,相同的话在对price进行排序呢,用这个不就没法实现了吗。这就需要提到了下面的Comparator接口
3.3 Comparator接口
照例先来看一下代码:
@FunctionalInterface
public interface Comparator<T> {
// 核心方法,用来比较两个对象,如果o1小于o2,返回负数;等于o2,返回0;大于o2返回正数
int compare(T o1, T o2);
// 好像很少用到,一般都用对象自带的equals
boolean equals(Object obj);
/**-----------下面的都是JDK1.8新增的接口,挑几个放进去----------*/
//返回反向排序比较器
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
//根据名字知道,先进行compare比较后,再进行一次比较
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
//对int类型的key进行比较
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
//返回正常顺序的比较器
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
}
一起来看一下如何使用,先来看一下JDK1.8以前的用法:
public class SimpleComparator implements Comparator<Item> {
@Override
public int compare(Item o1, Item o2) {
return o1.price - o2.price;
}
public static void main(String[] args) {
List<Item> items = Arrays.asList(new Item("banana", 200), new Item("apple", 400), new Item("Orange", 100));
Collections.sort(items, new SimpleComparator());
System.out.println(items);
}
}
上述main函数的输出是:
[Item{name='Orange', price=100}, Item{name='banana', price=200}, Item{name='apple', price=400}]
JDK1.8以前的用法要自己手动实现Comparator接口,然后调用Collections.sort(),传入实现类来完成排序,非常麻烦,而JDK1.8则相对来说简单了很多:
public static void main(String[] args) {
List<Item> items = Arrays.asList(new Item("banana", 200), new Item("apple", 400), new Item("Orange", 100));
Collections.sort(items, (Item a, Item b) -> a.price - b.price);
System.out.println(items);
}
甚至,我们可以不使用Collections.sort:
public static void main(String[] args) {
List<Item> items = Arrays.asList(new Item("banana", 100), new Item("Orange", 100), new Item("apple", 400), new Item("Orange", 50));
items.sort((Item a, Item b) -> a.price - b.price);
System.out.println(items);
//使用上面的thenComparing
items.sort(Comparator.comparing(Item::getName).thenComparing(Comparator.comparingInt(Item::getPrice)));
System.out.println("after using thenComparing: " + items);
}
上述main函数的输出:
[Item{name='orange', price=50}, Item{name='banana', price=100}, Item{name='orange', price=100}, Item{name='apple', price=400}]
after using thenComparing: [Item{name='apple', price=400}, Item{name='banana', price=100}, Item{name='orange', price=50}, Item{name='orange', price=100}]
四、NavigableMap接口源码解析
public interface NavigableMap<K,V> extends SortedMap<K,V> {
//返回键小于且最接近Key(不包含等于)的键值对,没有返回null
Map.Entry<K,V> lowerEntry(K key);
//返回小于且最接近(不包含等于)Key的键,没有返回null
K lowerKey(K key);
//返回键小于且最接近(包含等于)Key的键值对,没有返回null
Map.Entry<K,V> floorEntry(K key);
//返回小于且最接近(包含等于)Key的键,没有返回null
K floorKey(K key);
//返回大于且最接近(包含等于)给定key的键值对,没有返回null
Map.Entry<K,V> ceilingEntry(K key);
//同上
K ceilingKey(K key);
//返回大于且最接近(不包含等于)给定key的键值对
Map.Entry<K,V> higherEntry(K key);
//同上
K higherKey(K key);
//返回第一个Entry
Map.Entry<K,V> firstEntry();
//返回最后一个Entry
Map.Entry<K,V> lastEntry();
//移除并返回第一个Entry
Map.Entry<K,V> pollFirstEntry();
//同上
Map.Entry<K,V> pollLastEntry();
//返回map中包含的映射关系的逆序视图
NavigableMap<K,V> descendingMap();
//返回map中包含的键的NavigableSet视图。 set的迭代器按key的升序
NavigableSet<K> navigableKeySet();
//逆序
NavigableSet<K> descendingKeySet();
//根据fromKey和toKey来返回子map,两个boolean参数用于是否包含该key
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive);
//返回小于(或等于,根据inclusive)toKey的map
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
//返回大于(或等于,根据inclusive)fromKey的map
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
SortedMap<K,V> subMap(K fromKey, K toKey);
SortedMap<K,V> headMap(K toKey);
SortedMap<K,V> tailMap(K fromKey);
}
注意:上述返回的map与原map是相互影响的。
五、总结
本章分析了TreeMap的继承关系,给后面分析TreeMap作为铺垫。SortedMap和NavigableMap的接口中,包含了大量的返回Map的方法,这也是作为排序Map的一大特点吧。最后谢谢各位园友观看,与大家共同进步!
【JDK1.8】JDK1.8集合源码阅读——TreeMap(一)的更多相关文章
- 【JDK1.8】JDK1.8集合源码阅读——TreeMap(二)
一.前言 在前一篇博客中,我们对TreeMap的继承关系进行了分析,在这一篇里,我们将分析TreeMap的数据结构,深入理解它的排序能力是如何实现的.这一节要有一定的数据结构基础,在阅读下面的之前,推 ...
- 【JDK1.8】Java 8源码阅读汇总
一.前言 万丈高楼平地起,相信要想学好java,仅仅掌握基础的语法是远远不够的,从今天起,笔者将和园友们一起阅读jdk1.8的源码,并将阅读重点放在常见的诸如collection集合以及concu ...
- 【JDK1.8】JDK1.8集合源码阅读——IdentityHashMap
一.前言 今天我们来看一下本次集合源码阅读里的最后一个Map--IdentityHashMap.这个Map之所以放在最后是因为它用到的情况最少,也相较于其他的map来说比较特殊.就笔者来说,到目前为止 ...
- java1.7集合源码阅读: Stack
Stack类也是List接口的一种实现,也是一个有着非常长历史的实现,从jdk1.0开始就有了这个实现. Stack是一种基于后进先出队列的实现(last-in-first-out (LIFO)),实 ...
- java1.7集合源码阅读: Vector
Vector是List接口的另一实现,有非常长的历史了,从jdk1.0开始就有Vector了,先于ArrayList出现,与ArrayList的最大区别是:Vector 是线程安全的,简单浏览一下Ve ...
- 【JDK1.8】JDK1.8集合源码阅读——总章
一.前言 今天开始阅读jdk1.8的集合部分,平时在写项目的时候,用到的最多的部分可能就是Java的集合框架,通过阅读集合框架源码,了解其内部的数据结构实现,能够深入理解各个集合的性能特性,并且能够帮 ...
- 【JDK1.8】JDK1.8集合源码阅读——HashMap
一.前言 笔者之前看过一篇关于jdk1.8的HashMap源码分析,作者对里面的解读很到位,将代码里关键的地方都说了一遍,值得推荐.笔者也会顺着他的顺序来阅读一遍,除了基础的方法外,添加了其他补充内容 ...
- 【JDK1.8】JDK1.8集合源码阅读——ArrayList
一.前言 在前面几篇,我们已经学习了常见了Map,下面开始阅读实现Collection接口的常见的实现类.在有了之前源码的铺垫之后,我们后面的阅读之路将会变得简单很多,因为很多Collection的结 ...
- 【JDK1.8】JDK1.8集合源码阅读——LinkedList
一.前言 这次我们来看一下常见的List中的第二个--LinkedList,在前面分析ArrayList的时候,我们提到,LinkedList是链表的结构,其实它跟我们在分析map的时候讲到的Link ...
随机推荐
- DevOps之负载均衡
唠叨话 关于德语噢屁事的知识点,仅提供专业性的精华汇总,具体知识点细节,参考教程网址,如需帮助,请留言. <负载均衡LB(Load Balance)> 关于负载均衡器:知识与技能的层次(知 ...
- canvas图表详解系列(3):动态饼状图(原生Js仿echarts饼状图)
本章建议学习时间4小时 学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记) 学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步 ...
- ubuntu下MySQL修改root密码的多种方法,phpmyadmin空密码无法登陆的解决方法
phpmyadmin是默认不允许使用空密码的,所以若是在安装时没有设置密码,在登陆phpmyadmin时是个很头疼的问题 方法1是修改phpmyadmin的配置文件,这里不做推荐.. 方法2: php ...
- [Python] Codecombat 攻略 Sarven 沙漠 (1-43关)截止至30关
首页:https://cn.codecombat.com/play语言:Python 第二界面:Sarven沙漠(43关)时间:4-11小时内容:算术运算,计数器,while循环,break(跳出循环 ...
- JUnit5 安装与使用
虽然JUnit5 的测试版本早就出来了,但正式版直到几年9月份推出,目前最新版5.0.1.几乎所有的Java 开发人员都会使用JUnit 来做测试,但其实很多自动化测试人员也会使用Junit .目前, ...
- NOIP2012疫情控制(二分答案+倍增+贪心)
Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...
- Tomcat 笔记-目录简介
bin:启动和关闭tomcat的bat文件 conf:配置文件 server.xml该文件用于配置server相关的信息,比如tomcat启动的端口号,配置主机(Host) web.xml文件配置与w ...
- 学习SVG 重点汇总
什么是SVG? Δ SVG 指可伸缩矢量图形 (Scalable Vector Graphics) Δ SVG 用来定义用于网络的基于矢量的图形 Δ SVG使用XML格式来定义图形 Δ SVG ...
- python 文件相关知识
字符编码相关 什么是字符编码 字符编码的类型 字符编码的使用 python2和python里字符编码的区别 文件的相关 文件的基础操作 打开文件的模式 字符编码 什么是字符编码在计算机里只识别二进制, ...
- 微信小程序用setData修改数组或对象中的一个属性值
在page中有如下数组 data: { info:[ { name:"yuki", tou:"../img/head.jpg", zGong:130, gMon ...