JDK源码学习之TreeMap.java分析

TreeMap源码分析——基础分析

  常见的数据结构有数组、链表、树。集合类中有基于数组的ArrayList,基于链表的LinkedList,还有链表和数组结合的HashMap。
  Treemap基于红黑树实现。查看“键”或“键值对”时,他们会被排序(次序 由Comparable或Comparator决定)。
  TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
 
  在介绍TreeMap前先介绍Comparable和Comparator接口。
  Comparable接口:
    public interface  Comparable<T>{
        public int compareTo(T o);
    }
    Comparable接口支持泛型,只有一个方法,该方法返回负数、零、正数分别表示当前对象“小于”、“等于”、“大于”传入对象o。
    Comparator接口:
    public interface Comparator<T>{
        int compare(T o1, T o2);
        boolean equals(Object obj);
    }
      compare(T o1, T o2)方法比较o1和o2两个对象,o1“大于”o2,返回正数,相等返回零,“小于”返回负数。
      equals(Object obj)返回true的唯一情况是obj也是一个比较器(Comparator)并且比较结果和此比较器的结果的大小次序是一致的。
      即comp1.equals(comp2)意味着sgn(comp1.compare(o1, *o2))==sgn(comp2.compare(o1,o2))。
      符号sgn(expression)表示数学上的sigmoid函数,该函数根据expression的值是负数、零或者正数,分别返回-1、0或1.
      
      小结一下,实现Comparable结构的类可以和其他对象进行比较,即实现Comparable可以进行比较的类。、
      而实现Comparator接口的类是比较器,用于比较两个对象的大小。
      
    TreeMap
        TreeMap类定义:
          public class TreeMap<K, V> extends AbstractMap<K, V> implements NavigableMap<K, V>, Cloneable, java.io.Serializable
          NavigableMap接口扩展的SortedMap,具有了针对给定搜索目标返回最接近匹配项的导航方法。
          方法lowerEntry、floorEntry、ceilingEntry和higherEntry分别返回与小于、小于等于、大于等于、大于给定键的键关联的Map.Entry对象,如果不存在这样的键,则返回null。
          类似地,方法lowerKey、floorKey、ceilingKey和higherKey只返回关联的键。
          所有这些方法是为查找条目而不是遍历条目而设计的。
          
          TreeMap的属性有:
          
 //用于保持顺序的比较器,如果为空,使用自然序保持Key的顺序
            private final Comparator<? super K> comparator;
            //根节点
            private transient Entry<K,V> root = null;
            //树中的结点数量
            private transient int size = 0;
            //多次在集合类中提到了,用于记录结构的改变次数
            private transient int modCount = 0;

            
          TreeMap中的put()方法和其他Map的put()方法一样,向Map中加入键值对,若原先“键(Key)”已经存在则替换“值value”,并返回原先的值。
                在put(K key, V value)方法的末尾调用了fixAfterInsertion(Entry<K, V> x)方法,这个方法负责在插入结点后调整树结构和着色,以满足红黑树的要求。
                    1.每个结点或者是红色,或者是黑色
                    2.根是黑色的
                    3.如果一个结点是红色的,那么它的子节点必须是黑色的
                    4.一个结点到一个null引用的每一条路径必须包含相同数量的黑色结点。
                注意,红黑树不是严格的平衡二叉树,它并不严格的保证左右子树的高度差不超过1,但红黑树高度依然是平均log(n),
                  且最坏情况高度不会超过2log(n),所以它算是平衡树。
                  fixAfterInsertion(Entry<K, V> x)方法涉及到了左旋和右旋操作
                  
          TreeMap中的get(Object key)通过Key获取对应的value,它通过调用getEntry(Object key)获取结点,
              若结点为null则返回null,否则返回结点的value值。
              getEntry函数主要是处理实现了可比较接口的情况,而有比较器的情况则是调用了getEntryUsingComparator(Object key).
              
          remove(Object key)只是获取要删除的结点并返回被删除结点的value。真正实现删除结点的内容是在deleteEntry(Entry e)中,设计到树结构的调整。
            deleteEntry(Entry e)方法中主要有两个方法调用需要分析:successor(Entry<K,V> t)和fixAfterDeletion(Entry<K,V> x)。
              successor(Entry<K,V> t)返回指定结点的后继结点。分三种情况处理,
                  第一,t结点是个空结点:返回null;
                  第二,t有右孩子:找到t的右孩子的最左子孙结点,如果右孩子没有左孩子则返回右结点,否则返回找到的最左子孙结点;
                  第三,t没有右孩子:沿着向上(向根节点方向)找到第一个自身是一个左孩子的结点或根节点,返回找到的结点
            与添加结点之后的修复类似,TreeMap删除结点后也需要进行类似的修复操作,通过这种修复来保证该排序二叉树依然满足红黑树特征。
                删除之后的修复有fixAfterDeletion(Entry<K,V> x)方法提供。
                
          clear()方法只是记录结构修改次数,将Size修改为0,将root设置为null,这样就没法通过root访问树的其他结点,所以树的内容会被GC回收。
          
          containsKey(Object  key)判断获取Key对应的结点是否为空,调用getEntry(Object key)方法获得Key对应的对象。
          
          containsValue(Object value)涉及到了getFirstEntry()方法和successor(Entry<K,V> e)方法。getFirstEntry()是获取第一个结点,
              successor(Entry<K,V> e)是获取结点e的后继结点,在for循环中配合使用getFirstEntry()方法和successor(Entry<K,V> e)及e!=null是遍历树的一种方法。
              getFirstEntry()实际上是获取整棵树中“最左”的结点(第一个结点具体指哪一个结点和树的遍历次序有关,如果是先根遍历,则第一个结点是根节点)。
                    又因为红黑树是排序的树,所以“最左”的结点也是值最小的结点。
              getLastEntry(),获取最右的结点
              
          TreeMap中还提供了获取并移除最小和最大结点的两个方法:pollFirstEntry()和pollLastEntry(),分别通过getFirstEntry()和getLastEntry()获取结点,
              ExportEntry(TreeMap.Entry<K, V> e)是创建一个用于返回的删除结点对象。
              
 static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
                  return e == null? null :
                  new AbstractMap.SimpleImmutableEntry<K,V>(e);
                 }

              返回了一个SimpleImmutableEntry对象,调用的构造方法如下:
              
 public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
                    this.key   = entry.getKey();
                    this.value = entry.getValue();
                }

              可以看到返回的结点内容只包含Key和value。
              而其他具体的获取键、值、键值对的方法。
               
  public Map.Entry<K,V> ceilingEntry(K key) {
                       return exportEntry(getCeilingEntry(key));
                   }
                   public K ceilingKey(K key) {
                       return keyOrNull(getCeilingEntry(key));
                   }

              上面这两个方法只是对exportEntry和keyOrNull的调用。keyOrNull根据传入的Entry是否为null,选择返回null或Entry的Key
               
  // 获取最小的节点的key
                  public K firstKey() {
                      return key(getFirstEntry());
                  }
                  // 获取最大节点的key
                  public K lastKey() {
                      return key(getLastEntry());
                  }
                  // 获取最小的键值对
                 public Map.Entry<K,V> firstEntry() {
                     return exportEntry(getFirstEntry());
                 }
                 // 获取最大的键值对
                 public Map.Entry<K,V> lastEntry() {
                     return exportEntry(getLastEntry());
                 }
             
              getFloorEntry和getHigherEntry方法遍历和寻找结点的方法类似,区别在于getFloorEntry寻找的是小于等于,优先返回小于的结点,
                而getHigherEntry寻找的是严格大于的结点,不包括等于的情况。
              
              
          
          
              
          
          
          

你可能感兴趣的:(jdk,源码,TreeMap)