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。结果例如以下:

Java集合系列之TreeMap源代码分析的更多相关文章
- Java 集合系列 12 TreeMap
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 17 TreeSet
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 16 HashSet
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 15 Map总结
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 14 hashCode
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java 集合系列 13 WeakHashMap
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
随机推荐
- Uva 11077 Find the Permutation
可以发现最优的方案就是一个循环节内互换. 所以一个有n个元素,c个循环节的置换的交换次数(最少)是n-c. 然后就可以递推了,把i插入到前i-1个元素构成的置换中,要么新成立一个循环,要么加入到之前的 ...
- [Beijing2010组队]次小生成树Tree
小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小 ...
- [POI2015]Kinoman
题目大意: 给你一个长度为n的数列f,f中共有m种不同的数,每种数都有一个权值w[i]. 你可以选定一个f中的区间,定义区间的权值为这一区间只出现一次的数的权值和. 问权值最大的区间的权值是多少? 思 ...
- Litepal 数据库操作框架的使用 (火)
LitePal是GitHub上一款开源的Android数据库框架. 它採用了对象关系映射(ORM)的模式,将平时开发时最经常使用的一些数据库功能进行了封装.使得开发人员不用编写一行SQL语句就能够完毕 ...
- Timeline简单配置
Timeline是一个Jquery时间轴插件.效果如图 获取地址:https://github.com/ka215/jquery.timeline 配置 (1)html表头加入 <link re ...
- IntelliJ IDEA 取消控制台行数限制
在idea7之后的版本中取消了 控制台行数设置 选项,只能通过更改配置文件进行更改 在%安装目录%/bin中找到idea.properties文件,更改idea.cycle.buffer.size项值 ...
- HTML5 Canvas 画钟表
画钟表是2D画图的老生常谈,我也不能免俗弄了一个.代码如下: <!DOCTYPE html> <html lang="utf-8"> <meta ht ...
- 设置快速的debian源的方法:
1)设置临时源 vi /etc/apt/sources.list #添加以下一行到文件最后 deb http://http.us.debian.org/debian stable main 2)更新软 ...
- python中MySQLdb模块用法实例
篇文章主要介绍了python中MySQLdb模块用法,以实例形式详细讲述了MySQLdb模块针对MySQL数据库的各种常见操作方法,非常具有实用价值,需要的朋友可以参考下 本文实例讲述了python中 ...
- pugixml 的常见读写操作
pugixml github地址 : https://github.com/zeux/pugixml pugixml 可以在github上直接下载到源码,包括两个头文件(pugixml.hpp pu ...