TreeMap源码分析(jdk1.8)

来源:互联网 发布:数据库权限管理 编辑:程序博客网 时间:2024/06/05 04:53

TreeMap的基本概念:

  1. TreeMap集合是基于红黑树(Red-Black tree)的 NavigableMap实现。该集合最重要的特点就是可排序,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。这句话是什么意思呢?就是说TreeMap可以对添加进来的元素进行排序,可以按照默认的排序方式,也可以自己指定排序方式。
  2. 根据上一条,我们要想使用TreeMap存储并排序我们自定义的类(如User类),那么必须自己定义比较机制:一种方式是User类去实现Java.lang.Comparable接口,并实现其compareTo()方法。另一种方式是写一个类(如MyCompatator)去实现java.util.Comparator接口,并实现compare()方法,然后将MyCompatator类实例对象作为TreeMap的构造方法参数进行传参。
  3. TreeMap的实现是红黑树算法的实现,应该了解红黑树的基本概念。
    一、红黑树简介
    红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性。同时红黑树更是一颗自平衡的排序二叉树。
    1、每个节点都只能是红色或者黑色
    2、根节点是黑色
    3、每个叶节点(NIL节点,空节点)是黑色的。
    4、如果一个结点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色结点。
    5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
    具体实现参考:http://www.cnblogs.com/fanzhidongyzby/p/3187912.html
    http://blog.csdn.net/eric491179912/article/details/6179908

/************************************TreeMap********************************/
TreeMap的定义如下:

public class TreeMap<K,V>    extends AbstractMap<K,V>    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

TreeMap继承AbstractMap,实现NavigableMap、Cloneable、Serializable三个接口。其中AbstractMap表明TreeMap为一个Map即支持key-value的集合, NavigableMap(更多)则意味着它支持一系列的导航方法,具备针对给定搜索目标返回最接近匹配项的导航方法 。
TreeMap中同时也包含了如下几个重要的属性:

//比较器,因为TreeMap是有序的,通过comparator接口我们可以对TreeMap的内部排序进行精密的控制        private final Comparator<? super K> comparator;        //TreeMap红-黑节点,为TreeMap的内部类        private transient Entry<K,V> root = null;        //容器大小        private transient int size = 0;        //TreeMap修改次数        private transient int modCount = 0;        //红黑树的节点颜色--红色        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;    // 指向左子树的引用(指针)        Entry<K,V> right;   // 指向右子树的引用(指针)        Entry<K,V> parent;  // 指向父节点的引用(指针)        boolean color = BLACK; //     }}

类构造方法

    public TreeMap() {   // 1,无参构造方法        comparator = null; // 默认比较机制    }    public TreeMap(Comparator<? super K> comparator) { // 2,自定义比较器的构造方法        this.comparator = comparator;    }    public TreeMap(Map<? extends K, ? extends V> m) {  // 3,构造已知Map对象为TreeMap        comparator = null; // 默认比较机制        putAll(m);    }    public TreeMap(SortedMap<K, ? extends V> m) { // 4,构造已知的SortedMap对象为TreeMap        comparator = m.comparator(); // 使用已知对象的构造器        try {            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);        } catch (java.io.IOException cannotHappen) {        } catch (ClassNotFoundException cannotHappen) {        }    }

TreeMap put()方法实现分析
在TreeMap的put()的实现方法中主要分为两个步骤,第一:构建排序二叉树,第二:平衡二叉树。
对于排序二叉树的创建,其添加节点的过程如下:

  1. 以根节点为初始节点进行检索。
  2. 与当前节点进行比对,若新增节点值较大,则以当前节点的右子节点作为新的当前节点。否则以当前节点的左子节点作为新的当前节点。
  3. 循环递归2步骤知道检索出合适的叶子节点为止。
  4. 将新增节点与3步骤中找到的节点进行比对,如果新增节点较大,则添加为右子节点;否则添加为左子节点。
public V put(K key, V value) {             //用t表示二叉树的当前节点              Entry<K,V> t = root;              //t为null表示一个空树,即TreeMap中没有任何元素,直接插入              if (t == null) {                  //比较key值,空树还需要比较、排序?                compare(key, key); // type (and possibly null) check                  //将新的key-value键值对创建为一个Entry节点,并将该节点赋予给root                  root = new Entry<>(key, value, null);                  //容器的size = 1,表示TreeMap集合中存在一个元素                  size = 1;                  //修改次数 + 1                  modCount++;                  return null;              }              int cmp;     //cmp表示key排序的返回结果              Entry<K,V> parent;   //父节点              // split comparator and comparable paths              Comparator<? super K> cpr = comparator;    //指定的排序算法              //如果cpr不为空,则采用既定的排序算法进行创建TreeMap集合              if (cpr != null) {                  do {                      parent = t;      //parent指向上次循环后的t                      //比较新增节点的key和当前节点key的大小                      cmp = cpr.compare(key, t.key);                      //cmp返回值小于0,表示新增节点的key小于当前节点的key,则以当前节点的左子节点作为新的当前节点                      if (cmp < 0)                          t = t.left;                      //cmp返回值大于0,表示新增节点的key大于当前节点的key,则以当前节点的右子节点作为新的当前节点                      else if (cmp > 0)                          t = t.right;                      //cmp返回值等于0,表示两个key值相等,则新值覆盖旧值,并返回新值                      else                          return t.setValue(value);                  } while (t != null);              }              //如果cpr为空,则采用默认的排序算法进行创建TreeMap集合              else {                  if (key == null)     //key值为空抛出异常                      throw new NullPointerException();                  /* 下面处理过程和上面一样 */                  Comparable<? super K> k = (Comparable<? super K>) key;                  do {                      parent = t;                      cmp = k.compareTo(t.key);                      if (cmp < 0)                          t = t.left;                      else if (cmp > 0)                          t = t.right;                      else                          return t.setValue(value);                  } while (t != null);              }              //将新增节点当做parent的子节点              Entry<K,V> e = new Entry<>(key, value, parent);              //如果新增节点的key小于parent的key,则当做左子节点              if (cmp < 0)                  parent.left = e;            //如果新增节点的key大于parent的key,则当做右子节点              else                  parent.right = e;              /*               *  上面已经完成了排序二叉树的的构建,将新增节点插入该树中的合适位置               *  下面fixAfterInsertion()方法就是对这棵树进行调整、平衡,具体过程参考上面的五种情况               */              fixAfterInsertion(e);              //TreeMap元素数量 + 1              size++;              //TreeMap容器修改次数 + 1              modCount++;              return null;          }  

上面代码中do{}代码块是实现排序二叉树的核心算法,通过该算法我们可以确认新增节点在该树的正确位置。找到正确位置后将插入即可,这样做了其实还没有完成,因为我知道TreeMap的底层实现是红黑树,红黑树是一棵平衡排序二叉树,普通的排序二叉树可能会出现失衡的情况,所以下一步就是要进行调整。fixAfterInsertion(e); 调整的过程务必会涉及到红黑树的左旋、右旋、着色三个基本操作。代码如下:

/**      * 新增节点后的修复操作      * x 表示新增节点      */       private void fixAfterInsertion(Entry<K,V> x) {              x.color = RED;    //新增节点的颜色为红色              //循环 直到 x不是根节点,且x的父节点不为红色              while (x != null && x != root && x.parent.color == RED) {                  //如果X的父节点(P)是其父节点的父节点(G)的左节点                  if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {                      //获取X的叔节点(U)                      Entry<K,V> y = rightOf(parentOf(parentOf(x)));                      //如果X的叔节点(U) 为红色(情况三)                      if (colorOf(y) == RED) {                               //将X的父节点(P)设置为黑色                          setColor(parentOf(x), BLACK);                          //将X的叔节点(U)设置为黑色                          setColor(y, BLACK);                          //将X的父节点的父节点(G)设置红色                          setColor(parentOf(parentOf(x)), RED);                          x = parentOf(parentOf(x));                      }                      //如果X的叔节点(U为黑色);这里会存在两种情况(情况四、情况五)                      else {                             //如果X节点为其父节点(P)的右子树,则进行左旋转(情况四)                          if (x == rightOf(parentOf(x))) {                              //将X的父节点作为X                              x = parentOf(x);                              //右旋转                              rotateLeft(x);                          }                          //(情况五)                          //将X的父节点(P)设置为黑色                          setColor(parentOf(x), BLACK);                          //将X的父节点的父节点(G)设置红色                          setColor(parentOf(parentOf(x)), RED);                          //以X的父节点的父节点(G)为中心右旋转                          rotateRight(parentOf(parentOf(x)));                      }                  }                  //如果X的父节点(P)是其父节点的父节点(G)的右节点                  else {                      //获取X的叔节点(U)                      Entry<K,V> y = leftOf(parentOf(parentOf(x)));                    //如果X的叔节点(U) 为红色(情况三)                      if (colorOf(y) == RED) {                          //将X的父节点(P)设置为黑色                          setColor(parentOf(x), BLACK);                          //将X的叔节点(U)设置为黑色                          setColor(y, BLACK);                          //将X的父节点的父节点(G)设置红色                          setColor(parentOf(parentOf(x)), RED);                          x = parentOf(parentOf(x));                      }                    //如果X的叔节点(U为黑色);这里会存在两种情况(情况四、情况五)                      else {                          //如果X节点为其父节点(P)的右子树,则进行左旋转(情况四)                          if (x == leftOf(parentOf(x))) {                              //将X的父节点作为X                              x = parentOf(x);                             //右旋转                              rotateRight(x);                          }                          //(情况五)                          //将X的父节点(P)设置为黑色                          setColor(parentOf(x), BLACK);                          //将X的父节点的父节点(G)设置红色                          setColor(parentOf(parentOf(x)), RED);                          //以X的父节点的父节点(G)为中心右旋转                          rotateLeft(parentOf(parentOf(x)));                      }                  }              }              //将根节点G强制设置为黑色              root.color = BLACK;          }  

TreeMap delete()方法:针对于红黑树的增加节点而言,删除显得更加复杂,使原本就复杂的红黑树变得更加复杂。同时删除节点和增加节点一样,同样是找到删除的节点,删除之后调整红黑树。但是这里的删除节点并不是直接删除,而是通过走了“弯路”通过一种捷径来删除的:找到被删除的节点D的子节点C,用C来替代D,不是直接删除D,因为D被C替代了,直接删除C即可。所以这里就将删除父节点D的事情转变为了删除子节点C的事情,这样处理就将复杂的删除事件简单化了。子节点C的规则是:右分支最左边,或者 左分支最右边的。
一帮以getEntry()方法为基础的获取元素的方法,其中包括containsKey(),get(),remove()等。

final Entry<K,V> getEntry(Object key) {        // 如果有自定义比较器对象,就按照自定义规则遍历二叉树        if (comparator != null)            return getEntryUsingComparator(key);        if (key == null)            throw new NullPointerException();        @SuppressWarnings("unchecked")            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)                p = p.left;            else if (cmp > 0)                p = p.right;            else                return p;        }        return null;    }

一帮以getFirstEntry(),getLastEntry()为基础的获取头和尾元素的方法,其中包括:firstKey(),lastKey();firstEntry(),lastEntry();pollFirstEntry(),pollLastEntry()

    final Entry<K,V> getFirstEntry() { // 获取第一个元素也就是最小的元素,一直遍历左子树        Entry<K,V> p = root;        if (p != null)            while (p.left != null)                p = p.left;        return p;    }    final Entry<K,V> getLastEntry() { // 获取最后个元素也就是最大的元素,一直遍历右子树        Entry<K,V> p = root;        if (p != null)            while (p.right != null)                p = p.right;        return p;    }

treeMap只能根据key值进行排序,根据value进行排序可以参考下面方法:
http://blog.csdn.net/qq_23211905/article/details/72627046

原创粉丝点击