天天看点

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

版权声明:尊重博主原创文章,转载请注明出处哦~http://blog.csdn.net/eson_15/article/details/51217741

前面讨论完了hashmap和hashtable的源码,这一节我们来讨论一下treemap。先从整体上把握treemap,然后分析其源码,深入剖析treemap的实现。

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

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:

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

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。

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

private transient entry<k,v> root = null;  

        然后我们看看entry实体类的实现:

        先看一下entry实体类:

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

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(),其他的放到源码里分析:

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

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对象进行包装的:

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

static <k,v> map.entry<k,v> exportentry(treemap.entry<k,v> e) {  

    return (e == null) ? null :  

        new abstractmap.simpleimmutableentry<>(e);  

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

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的其他类似的方法我就不一一赘述了,我放到源代码里分析,都不难理解。如下:

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

/*********************** 与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里的成员属性。

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

/********************* 成员属性 ****************************/  

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方法:

java集合框架10——TreeMap和源码分析(一) 1. TreeMap简介 2. TreeMap的源码分析(基于JDK1.7)

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方法。

        如有错误之处,欢迎留言指正~

_____________________________________________________________________________________________________________________________________________________

-----乐于分享,共同进步!