TreeMap与TreeSet的源码分析

1、TreeMap源码

  1、属性部分:  

 private final Comparator<? super K> comparator;//比较器

 private transient Entry<K,V> root;//根节点

private transient int size = 0;//大小

private transient int modCount = 0;//结构修改次数

定义一个静态内部对象用以存储:
 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;
        ...
        重写了equals、hashCode、toString方法等
View Code

  2、构造器部分:

 public TreeMap() {//无参构造
        comparator = null;
    }

 public TreeMap(Comparator<? super K> comparator) {//带比较器构造
        this.comparator = comparator;
    }

public TreeMap(Map<? extends K, ? extends V> m) 
public TreeMap(SortedMap<K, ? extends V> m)
View Code

  3、put方法:

public V put(K key, V value) {
        Entry<K,V> t = root;
        //如果根节点为空,设置该元素为根节点;
        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;//comparator来自哪?构造器中;
        //如果传入对象自带比较器:
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        //无自带比较器,使用默认的比较机制:
        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);//注意key是实现比较器的对象
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }                
View Code

  注意:

  <1>:首先,如果想往TreeMap中存放Entry<K,V>,那么其K必须是实现compareTo方法的,那么K所对应的类需要implements comparable接口。当然常用类中已经重写了这个方法,所以我们可以直接使用:TreeMap.put(int a,String b)等;

  <2>:TreeMap是基于红黑树的数据结构,TreeMap中判断新入元素的存储位置时,只需要通过compareTo方法判断两个对象的Key是否相同:相同,则更新Value,返回旧值Value;不同,则插在左或右子节点上,不同于HashMap通过Key的hashCode判断存储位置,所以即使在TreeMap中没有hashCode和equals方法的重写,对于put也没有影响。

  <3>:put入新节点后,TreeMap需要调整整个红黑树的结构。后续学习......;

2、TreeSet的add源码

   public boolean add(E e) { return m.put(e, PRESENT)==null; } 

  看到add只需要接收一个参数e,m是一个 private transient NavigableMap<E,Object> m; ,接口,其实现类是TreeMap,因此知道,TreeSet的add通过调用TreeMap的put方法实现添加节点操作,但是由于add只接收一个形参e,故TreeSet生成了一个PRESENT以构成<K,V>的形式,PRESENT是 private static final Object PRESENT = new Object(); 完全是用来凑得嘛。

  但是一定要注意:TreeMap的put中当通过comparedTo判断两个节点的Key相等时,会更新Value。而TreeSet的Value并没有什么作用,故可Key还是原来的Key,并没有发生改变。

总结:如果要插入新元素到TreeMap或TreeSet中:

  1、TreeMap中若newKey.compareTo(oldkey)返回值是0,那么Key不变,更新Value的值为newValue,返回oldValue。

  2、TreeSet中,简单可以理解为不更新(因为我们只用Key)。

实例:

  

public class C1 implements Comparable {
    String attr1="attr";
    static  int  attr2=2;//注意static
    public C1(String attr1,int attr2){
        this.attr1=attr1;
        this.attr2=attr2;
    }
    public String getAttr1() {
        return attr1;
    }
    public void setAttr1(String attr1) {
        this.attr1 = attr1;
    }
    public int getAttr2() {
        return attr2;
    }
    public  void setAttr2(int attr2) {
        this.attr2 = attr2;
    }    
    
    @Override
    public int compareTo(Object that) {
//        return ((C1)that).attr2-((C1)this).getAttr2();
        return 0;
    }
    public String toString(){return "attr1="+attr1+",attr2="+attr2+";";}
    public boolean equals(Object that){
        return attr1==((C1)that).getAttr1();
    }
    public int hashCode(){return attr2;}
    
    public static void main(String[] args) {
        Set set=new TreeSet();
        
        C1 c1=new C1("sttr",4);
        set.add(c1);
        
        C1 c2=new C1("sttr",5);
        set.add(c2);
        
        set.add(new C1("sttr",5));
        
        set.add(new C1("sttr",3));
        set.add(new C1("sttr2",2));
        
        System.out.println(set);
    }
}
View Code
[attr1=sttr,attr2=2;]
//为什么是2?注意attr2是static的,最后一个new C1(..)会改变该所有对象的attr2值。TreeSet没有不会更新Key(就是这个对象new C1(..))
结果1
[attr1=sttr,attr2=4;]
//正常,没有更新
结果2
//compareTo第一行,attr2不带static
[attr1=sttr,attr2=5;, attr1=sttr,attr2=4;, attr1=sttr,attr2=3;, attr1=sttr2,attr2=2;]
结果3
//compareTo第一行,attr2带static

[attr1=sttr,attr2=2;]
结果4
原文地址:https://www.cnblogs.com/whtblog/p/8950498.html