Java集合之TreeMap源码分析
一、概述
TreeMap是基于红黑树实现的。由于TreeMap实现了java.util.sortMap接口,集合中的映射关系是具有一定顺序的,该映射根据其键的自然顺序进行排序或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。另外TreeMap中不允许键对象是null。
1、什么是红黑树?
红黑树是一种特殊的二叉排序树,主要有以下几条基本性质:
- 每个节点都只能是红色或者黑色
- 根节点是黑色
- 每个叶子节点是黑色的
- 如果一个节点是红色的,则它的两个子节点都是黑色的
- 从任意一个节点到每个叶子节点的所有路径都包含相同数目的黑色节点
红黑树的具体原理分析和算法设计可参见博文:红黑树的原理分析和算法设计。
2、key的两种排序方式
自然排序:TreeMap的所有key必须实现Comparable接口,并且所有key应该是同一个类的对象,否则将会抛ClassCastException异常
指定排序:这种排序需要在构造TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的key进行排序
3、TreeMap类的继承关系
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
其中,NavigableMap接口是扩展的SortMap,具有了针对给定搜索目标返回最接近匹配项的导航方法。其方法 lowerEntry
、floorEntry
、ceilingEntry
和 higherEntry
分别返回与小于、小于等于、大于等于、大于给定键的键关联的 Map.Entry
对象,如果不存在这样的键,则返回 null
。类似地,方法 lowerKey
、floorKey
、ceilingKey
和 higherKey
只返回关联的键。所有这些方法是为查找条目而不是遍历条目而设计的。
二、TreeMap源码分析
1、存储结构
TreeMap是基于红黑树实现的,树的节点定义如下:
static final class Entry<K,V> implements Map.Entry<K,V>
{
//键
K key;
//值
V value;
//左孩子
Entry<K,V> left;
//右孩子
Entry<K,V> right;
//父节点
Entry<K,V> parent;
//节点颜色
boolean color = BLACK;
//构造函数
Entry(K key, V value, Entry<K,V> parent)
{
this.key = key;
this.value = value;
this.parent = parent;
}
......
}
2、构造函数
TreeMap有四种构造函数,分别对应不同的参数。
//1.使用键的自然顺序构造一个新的、空的树映射
public TreeMap()
{
comparator = null;
}
//2.构造一个新的、空的树映射,该映射根据给定比较器进行排序
public TreeMap(Comparator<? super K> comparator)
{
this.comparator = comparator;
}
/3.构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自然顺序 进行排序
public TreeMap(Map<? extends K, ? extends V> m)
{
comparator = null;
putAll(m);
}
//4.构造一个与指定有序映射具有相同映射关系和相同排序顺序的新的树映射
public TreeMap(SortedMap<K, ? extends V> m)
{
comparator = m.comparator();
try
{
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
}
catch (java.io.IOException cannotHappen)
{
}
catch (ClassNotFoundException cannotHappen)
{
}
}
3、TreeMap常用方法
V put(K key,V value):将键值对(key,value)添加到TreeMap中
public V put(K key, V value)
{
Entry<K,V> t = root;
//若根节点为空,则以(key,value)为参数新建节点
if (t == null)
{
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator; //指定的排序算法
if (cpr != null)
{
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0) //表示新增节点的key小于当前及节点的key,则以当前节点的左子节点作为新的当前节点
t = t.left;
else if (cmp > 0) //表示新增节点的key大于当前及节点的key,则以当前节点的右子节点作为新的当前节点
t = t.right;
else
return t.setValue(value); //相等则覆盖旧值
} while (t != null);
}
//如果cpr为空,则采用默认的排序算法进行创建TreeMap集合
else
{
if (key == null)
throw new NullPointerException();
@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);
}
//将新增节点当做parent的子节点
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//插入新的节点后,调用fixAfterInsertion调整红黑树
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射关系的Set视图
public Set<Map.Entry<K,V>> entrySet()
{
EntrySet es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}
boolean remove(Object o): 如果此 TreeMap 中存在该键的映射关系,则将其删除
public boolean remove(Object o)
{
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
K key = entry.getKey();
if (!inRange(key))
return false;
TreeMap.Entry<K,V> node = m.getEntry(key);
if (node!=null && valEquals(node.getValue(),
entry.getValue()))
{
m.deleteEntry(node);
return true;
}
return false;
}
}
三、TreeMap应用示例代码
public class TreeMapDemo
{
public static void main(String[] args)
{
//使用键的自然顺序构造一个新的、空的树映射
TreeMap<String,String> tm=new TreeMap<>();
tm.put("001", "中国");
tm.put("003", "美国");
tm.put("002", "法国");
System.out.println("调用entrySet得到键值对集:");
Set<Entry<String, String>> result=tm.entrySet();
for(Entry<String, String> result2:result)
{
System.out.println(result2.getKey()+"---"+result2.getValue());
}
System.out.println("调用keySet得到键集:");
Set<String> result3=tm.keySet();
for(String str:result3)
{
System.out.println(str);
}
System.out.println("调用values得到值集:");
Collection result4=tm.values();
for(Object str:result4)
System.out.println(str); //新建一个带比较器的TreeMap
TreeMap<String,String> tm2=new TreeMap<>(new ComparatorDemo());
tm2.put("001", "中国");
tm2.put("003", "美国");
tm2.put("002", "法国");
Set<Entry<String, String>> result5=tm2.entrySet();
for(Entry<String, String> result2:result5)
{
System.out.println(result2.getKey()+"---"+result2.getValue());
}
}
}
首先按照键的自然顺序构建TreeMap,加入元素并遍历:
然后新建一个比较器类,实现Comparator接口
public class ComparatorDemo implements Comparator<String>
{ public int compare(String o1, String o2) {
return 1;
} }
在带比较器的tm2中,按照与tm1相同的顺序添加元素,此时再遍历tm2,结果如下:
这里的顺序是按照比较器的compare方法得来的。由于compare方法总是返回1,即大小均相同,所以tm2中key顺序为初始添加顺序。
Java集合之TreeMap源码分析的更多相关文章
- 死磕 java集合之TreeMap源码分析(四)-内含彩蛋
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 二叉树的遍历 我们知道二叉查找树的遍历有前序遍历.中序遍历.后序遍历. (1)前序遍历,先遍历 ...
- 死磕 java集合之TreeMap源码分析(一)- 内含红黑树分析全过程
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 TreeMap使用红黑树存储元素,可以保证元素按key值的大小进行遍历. 继承体系 Tr ...
- 死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 删除元素 删除元素本身比较简单,就是采用二叉树的删除规则. (1)如果删除的位置有两个叶子节点 ...
- 死磕 java集合之TreeMap源码分析(二)- 内含红黑树分析全过程
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 插入元素 插入元素,如果元素在树中存在,则替换value:如果元素不存在,则插入到对应的位置, ...
- 死磕 java集合之DelayQueue源码分析
问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...
- 死磕 java集合之PriorityBlockingQueue源码分析
问题 (1)PriorityBlockingQueue的实现方式? (2)PriorityBlockingQueue是否需要扩容? (3)PriorityBlockingQueue是怎么控制并发安全的 ...
- 死磕 java集合之PriorityQueue源码分析
问题 (1)什么是优先级队列? (2)怎么实现一个优先级队列? (3)PriorityQueue是线程安全的吗? (4)PriorityQueue就有序的吗? 简介 优先级队列,是0个或多个元素的集合 ...
- 死磕 java集合之CopyOnWriteArraySet源码分析——内含巧妙设计
问题 (1)CopyOnWriteArraySet是用Map实现的吗? (2)CopyOnWriteArraySet是有序的吗? (3)CopyOnWriteArraySet是并发安全的吗? (4)C ...
- 死磕 java集合之LinkedHashSet源码分析
问题 (1)LinkedHashSet的底层使用什么存储元素? (2)LinkedHashSet与HashSet有什么不同? (3)LinkedHashSet是有序的吗? (4)LinkedHashS ...
随机推荐
- C#6.0语言规范(一) 介绍
C#(发音为“See Sharp”)是一种简单,现代,面向对象,类型安全的编程语言.C#源于C语言系列,对C,C ++和Java程序员来说很熟悉.EC#International将EC#标准化为ECM ...
- 视频下载四大神器—如何下载优酷/爱奇艺/腾讯/B站超清无水印视频
视频下载四大神器—如何下载优酷/爱奇艺/腾讯/B站超清无水印视频 2018-07-11 | 标签»下载, 下载工具, 视频 又是视频下载,老生常谈的话题.阿刚同学已在乐软博客多次与大家分享推荐 ...
- css高级选择器&盒模型
css高级选择器&盒模型 1.组合选择器 群组选择器 /* 每个选择器为可以为三种基础选择器的任意一个,用逗号隔开,控制多个*/ div,.div,#div{ color:red } 后代(子 ...
- MySQL:binlog 和 redo log
[参考文章]:MySQL中Redo与Binlog顺序一致性问题? [参考文章]:极客时间 1. 数据更新时的日志处理流程 1.1 redo log(prepare状态) 此时SQL已经成功执行了,已经 ...
- USB协议介绍
这里有必要先说清楚一下USB2.0规范的由来.USB2.0技术规范是有由Compaq.Hewlett Packard.Intel.Lucent.Microsoft.NEC.Philips共同制定.发布 ...
- rest framework 尝鲜
安装 pip install djangorestframework 新建项目 python manage.py startapp idcs 添加模型(models.py) class Idcs(mo ...
- [视频]K8飞刀 SQL注入点脱库演示教程
K8飞刀 SQL注入点脱库演示动画教程 链接:https://pan.baidu.com/s/15gLTxiv9v1Te5QNFZnfV4A 提取码:eaa1
- 14-02 Java Math类,Random类,System类,BigDecimal类
Math类 Math的方法 package cn.itcast_01; /* * Math:用于数学运算的类. * 成员变量: * public static final double PI * pu ...
- 【Java基本功】一文了解Java中继承、封装、多态的细节
本节主要介绍Java面向对象三大特性:继承 封装 多态,以及其中的原理. 本文会结合虚拟机对引用和对象的不同处理来介绍三大特性的原理. 继承 Java中的继承只能单继承,但是可以通过内部类继承其他类来 ...
- react 字符串强转为html标签
react中,富文本编辑 从数据库取出来 是带标签的 字符串,需要强转为 节点 <div dangerouslySetInnerHTML={{ __html: this.state.obj.ht ...