zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    聚合物钽电容和普通钽电容的区别
    Java命令:Jstack
    计算并发用户数的五种方法
    LoadRunner脚本优化-加密与解密
    如何定位性能瓶颈
    loadrunner配置多台负载机设置
    nmon监控及简要分析
    Web系统大规模并发——电商秒杀与抢购
    大型网站的灵魂——性能
    mycat实战之性能测试
  • 原文地址:https://www.cnblogs.com/whtblog/p/8950498.html
Copyright © 2011-2022 走看看