版权声明:尊重博主原创文章,转载请注明出处哦~http://blog.csdn.net/eson_15/article/details/51217741
前面讨论完了hashmap和hashtable的源码,这一节我们来讨论一下treemap。先从整体上把握treemap,然后分析其源码,深入剖析treemap的实现。
java.lang.object
↳ java.util.abstractmap<k, v>
↳ java.util.treemap<k, v>
public class treemap<k,v>
extends abstractmap<k,v>
implements navigablemap<k,v>, cloneable, java.io.serializable {}
从继承关系可以看出,treemap继承与abstractmap,实现了navigablemap接口,意味着它支持一系列的导航方法,比如返回有序的key集合。它还实现了cloneable接口,意味着它能被克隆。另外也实现了serializable接口,表示它支持序列化。
treemap是基于红-黑树实现的,该映射根据其key的自然顺序进行排序,或者根据用户创建映射时提供的comarator进行排序,另外,treemap是非同步的。
我们先总览一下treemap都有哪些api:
entry<k, v> ceilingentry(k key)
k ceilingkey(k key)
void clear()
object clone()
comparator<? super k> comparator()
boolean containskey(object key)
navigableset<k> descendingkeyset()
navigablemap<k, v> descendingmap()
set<entry<k, v>> entryset()
entry<k, v> firstentry()
k firstkey()
entry<k, v> floorentry(k key)
k floorkey(k key)
v get(object key)
navigablemap<k, v> headmap(k to, boolean inclusive)
sortedmap<k, v> headmap(k toexclusive)
entry<k, v> higherentry(k key)
k higherkey(k key)
boolean isempty()
set<k> keyset()
entry<k, v> lastentry()
k lastkey()
entry<k, v> lowerentry(k key)
k lowerkey(k key)
navigableset<k> navigablekeyset()
entry<k, v> pollfirstentry()
entry<k, v> polllastentry()
v put(k key, v value)
v remove(object key)
int size()
sortedmap<k, v> submap(k frominclusive, k toexclusive)
navigablemap<k, v> submap(k from, boolean frominclusive, k to, boolean toinclusive)
navigablemap<k, v> tailmap(k from, boolean inclusive)
sortedmap<k, v> tailmap(k frominclusive)
从这些api中可以看出,总共可分为这几大类:跟entry相关的,跟key相关的以及跟map相关的,另外还有keyset和entryset等方法,下文详细讨论这些api。
private transient entry<k,v> root = null;
然后我们看看entry实体类的实现:
先看一下entry实体类:
private static final boolean red = false;
private static final boolean black = true;
//就是一个红黑树的节点
static final class entry<k,v> implements map.entry<k,v> {
k key;
v value;
entry<k,v> left = null; //左子节点
entry<k,v> right = null; //右子节点
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;
}
public k getkey() { //获得key
return key;
public v getvalue() { //获得value
return value;
public v setvalue(v value) { //设置value
v oldvalue = this.value;
return oldvalue;
public boolean equals(object o) { //key和value均相等才返回true
if (!(o instanceof map.entry))
return false;
map.entry<?,?> e = (map.entry<?,?>)o;
return valequals(key,e.getkey()) && valequals(value,e.getvalue());
public int hashcode() { //计算hashcode
int keyhash = (key==null ? 0 : key.hashcode());
int valuehash = (value==null ? 0 : value.hashcode());
return keyhash ^ valuehash;
public string tostring() { //重写tostring方法
return key + "=" + value;
}
从entry实体类中可以看出,treemap中的entry就是一个红黑树的节点。跟这个entry相关的方法都有firstentry()、lastentry()、lowerentry()、higherentry()、floorentry()、ceilingentry()。它们的原理都是类似的,我们只分析firstentry(),其他的放到源码里分析:
public map.entry<k,v> firstentry() {
return exportentry(getfirstentry());
//获得treemap里第一个节点(即根据key排序最小的节点),如果treemap为空,返回null
final entry<k,v> getfirstentry() {
entry<k,v> p = root;
if (p != null)
while (p.left != null)
p = p.left;
return p;
代码很简单,一直往下走,直到找到那个节点,然后返回即可。这里为什么不直接调用getfirtstentry(),而是对外提供firstentry()供外界调用呢?这就说到了exportentry()方法的作用了,因为如果直接调用getfirstentry()方法的话,返回的entry是可以被修改的,但是经过exportentry()方法包装过之后就不能修改了,所以这么做事防止用于修改返回的entry。我们来看看exportentry()方法是如何对entry对象进行包装的:
static <k,v> map.entry<k,v> exportentry(treemap.entry<k,v> e) {
return (e == null) ? null :
new abstractmap.simpleimmutableentry<>(e);
public static class simpleimmutableentry<k,v>
implements entry<k,v>, java.io.serializable
{
private static final long serialversionuid = 7138329143949025153l;
private final k key;
private final v value;
public simpleimmutableentry(k key, v value) { //通过key和value初始化对象
this.key = key;
public simpleimmutableentry(entry<? extends k, ? extends v> entry) { //通过传进来一个entry初始化对象
this.key = entry.getkey();
this.value = entry.getvalue();
public k getkey() {
public v getvalue() {
public v setvalue(v value) { //!!!关键地方在这里,不能setvalue,否则会抛出unsupportedoperationexception异常
throw new unsupportedoperationexception();
public boolean equals(object o) {
map.entry e = (map.entry)o;
return eq(key, e.getkey()) && eq(value, e.getvalue());
public int hashcode() {
return (key == null ? 0 : key.hashcode()) ^
(value == null ? 0 : value.hashcode());
//重写了tostring方法,返回key=value形式
public string tostring() {
从上面代码中可以看出,被这个类包装过的entry是不允许被修改内容的,这也就是为什么treemap类不直接把getfirstentry()方法暴露出去,而是提供了firstentry()供外界调用的原因。关于entry的其他类似的方法我就不一一赘述了,我放到源代码里分析,都不难理解。如下:
/*********************** 与entry相关的方法 ******************************/
//获取treemap中键为key对应的节点
final entry<k,v> getentry(object key) {
// 若比较器不为null,则通过getentryusingcomparator来获得
if (comparator != null)
return getentryusingcomparator(key);
if (key == null)
throw new nullpointerexception();
comparable<? super k> k = (comparable<? super k>) key;
entry<k,v> p = root; //若没有比较器,则正常往下走
while (p != null) {
int cmp = k.compareto(p.key);
if (cmp < 0)
else if (cmp > 0)
p = p.right;
else
return p; //找到了就返回
return null;
//使用比较器获得与key对应的entry
final entry<k,v> getentryusingcomparator(object key) {
k k = (k) key;
comparator<? super k> cpr = comparator;
if (cpr != null) {
entry<k,v> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
/*--------------------------------------------------------*/
/*-----------------------------------------------*/
public map.entry<k,v> lastentry() {
return exportentry(getlastentry());
//获得treemap里最后一个节点(根据key排序最大的节点),如果treemap为空,返回null
final entry<k,v> getlastentry() {
while (p.right != null)
/*------------------------------------------------*/
//弹出第一个节点,即删除
public map.entry<k,v> pollfirstentry() {
entry<k,v> p = getfirstentry();
map.entry<k,v> result = exportentry(p);
deleteentry(p);
return result;
//弹出最后一个节点,即删除
public map.entry<k,v> polllastentry() {
entry<k,v> p = getlastentry();
/*-------------------------------------------------*/
public map.entry<k,v> floorentry(k key) {
return exportentry(getfloorentry(key));
public map.entry<k,v> ceilingentry(k key) {
return exportentry(getceilingentry(key));
//获取treemap中不小于key的最小的节点;
//若不存在(即treemap中所有节点的键都比key大),就返回null
final entry<k,v> getceilingentry(k key) {
int cmp = compare(key, p.key);
if (cmp < 0) { //情况1. 若p.key > key
if (p.left != null) //若p有左子节点
p = p.left; //往左下走
return p; //否则返回p
} else if (cmp > 0) { //情况2:p.key < key
if (p.right != null) {
} else {
// 若 p 不存在右孩子,则找出 p 的后继节点,并返回
// 注意:这里返回的 “p的后继节点”有2种可能性:第一,null;第二,treemap中大于key的最小的节点。
// 理解这一点的核心是,getceilingentry是从root开始遍历的。
// 若getceilingentry能走到这一步,那么,它之前“已经遍历过的节点的key”都 > key。
// 能理解上面所说的,那么就很容易明白,为什么“p的后继节点”又2种可能性了。
entry<k,v> parent = p.parent;
entry<k,v> ch = p;
while (parent != null && ch == parent.right) {
ch = parent;
parent = parent.parent;
}
return parent;
}
} else //情况3:p.key = key
return p;
// 获取treemap中不大于key的最大的节点;
// 若不存在(即treemap中所有节点的键都比key小),就返回null
// getfloorentry的原理和getceilingentry类似,这里不再多说。
final entry<k,v> getfloorentry(k key) {
if (cmp > 0) {
if (p.right != null)
} else if (cmp < 0) {
if (p.left != null) {
while (parent != null && ch == parent.left) {
} else
/*--------------------------------------------------*/
public map.entry<k,v> lowerentry(k key) {
return exportentry(getlowerentry(key));
public map.entry<k,v> higherentry(k key) {
return exportentry(gethigherentry(key));
}
// 获取treemap中大于key的最小的节点。
// 若不存在,就返回null。
// 请参照getceilingentry来对gethigherentry进行理解。
final entry<k,v> getlowerentry(k key) {
} else {
// 获取treemap中小于key的最大的节点。
// 请参照getceilingentry来对getlowerentry进行理解。
final entry<k,v> gethigherentry(k key) {
if (cmp < 0) {
if (p.left != null)
/*---------------------------------------------------*/
分析完了entry实体相关的源码后,我们来看看treemap里的成员属性。
/********************* 成员属性 ****************************/
private final comparator<? super k> comparator; //比较器
private transient entry<k,v> root = null; //实体对象
private transient int size = 0; //红黑树节点个数,即entry数
private transient int modcount = 0; //修改次数
/*********************** 构造方法 **************************/
public treemap() { //默认构造方法
comparator = null;
public treemap(comparator<? super k> comparator) { //带比较器的构造方法
this.comparator = comparator;
public treemap(map<? extends k, ? extends v> m) { //带map的构造方法,map会为treemap的子类
putall(m);
////带sortedmap的构造方法,sortedmap会为treemap的子类
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) {
我们可以看出,treemap有四个构造函数,这里分析一下第三个构造函数,内部调用了putall方法,我们看一下putall方法:
public void putall(map<? extends k, ? extends v> map) {
int mapsize = map.size();//获取map的大小
//如果treemap大小是0,且map的大小不为0,且map是已排序的key-value对,才执行下面的程序
if (size==0 && mapsize!=0 && map instanceof sortedmap) {
comparator c = ((sortedmap)map).comparator();
if (c == comparator || (c != null && c.equals(comparator))) {
++modcount;
try {
buildfromsorted(mapsize, map.entryset().iterator(),
null, null);
} catch (java.io.ioexception cannothappen) {
} catch (classnotfoundexception cannothappen) {
return;
//否则调用abstractmap中的putall()
//abstractmap中的putall()又会调用treemap的put()方法
super.putall(map);
在putall方法内部,会先进行判断,如果treemap是空的,且传进来的map符合条件,则执行if内的语句,然后调用buildfromsorted方法(后面放到源码中分析)来put进去,否则调用父类的putall方法,父类的putall方法则直接调用treemap的put方法。
如有错误之处,欢迎留言指正~
_____________________________________________________________________________________________________________________________________________________
-----乐于分享,共同进步!