在平常的工做中,相比较与
HashMap
而言,TreeMap
的使用会少不少,即便在某些场景,须要使用到排序的Map时,也更多的是选择LinkedHashMap
,那么这个TreeMap
究竟是个怎样的容器,又适用于什么样的应用场景呢?java
分析数据结构,最好的方式无疑是google+baidu+源码了数据结构
看到源码第一眼,就会发现与HashMap不一样的是 TreeMap 实现的是 NavigableMap
, 而不是直接实现 Map
学习
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable { // .... }
有必要仔细看下这个 NavigableMap
,到底有些什么特殊之处this
继承体系: Map -> SortMap -> NavigbleMap
google
其中 SortMap
新增了下面几个接口,目前也不知道具体有啥用,先翻译下源码注释.net
// 既然叫作SortMap, 要排序的话,固然须要一个比较器了 Comparator<? super K> comparator(); SortedMap<K,V> subMap(K fromKey, K toKey); // 源码注释: 返回比Map中key比参数toKey小的全部kv对 SortedMap<K,V> headMap(K toKey); // 源码注释:返回比Map中key比参数fromKey大或相等的全部kv对 SortedMap<K,V> tailMap(K fromKey); K firstKey(); K lastKey();
接着就是 NavigableMap
定义的接口翻译
// 返回Map中比传入参数key小的kv对中,key最大的一个kv对 Map.Entry<K,V> lowerEntry(K key); K lowerKey(K key); // 返回Map中比传入参数key小或相等的kv对中,key最大的一个kv对 Map.Entry<K,V> floorEntry(K key); K floorKey(K key); // 返回Map中比传入参数key大或相等的kv对中,key最小的一个kv对 Map.Entry<K,V> ceilingEntry(K key); K ceilingKey(K key); // 返回Map中比传入参数key大的kv对中,key最小的一个kv对 Map.Entry<K,V> higherEntry(K key); K higherKey(K key); Map.Entry<K,V> firstEntry(); Map.Entry<K,V> lastEntry(); Map.Entry<K,V> pollFirstEntry(); NavigableMap<K,V> descendingMap(); NavigableSet<K> navigableKeySet(); NavigableSet<K> descendingKeySet();
基本上这两个接口就是提供了一些基于排序的获取kv对的方式code
看下内部的成员变量,发现可能涉及到数据结构的就只有下面的这个root了blog
private transient Entry<K,V> root;
结合 TreeMap
的命名来看,底层的结构多半就真的是Tree
了,有树的根节点,通常来说遍历都是没啥问题的排序
接下来看下 Entry的实现
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; /** * Make a new cell with given key, value, and parent, and with * {@code null} child links, and BLACK color. */ Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } /** * Returns the key. * * @return the key */ public K getKey() { return key; } /** * Returns the value associated with the key. * * @return the value associated with the key */ public V getValue() { return value; } /** * Replaces the value currently associated with the key with the given * value. * * @return the value associated with the key before this method was * called */ public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { 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() { int keyHash = (key==null ? 0 : key.hashCode()); int valueHash = (value==null ? 0 : value.hashCode()); return keyHash ^ valueHash; } public String toString() { return key + "=" + value; } }
从Entry的内部成员变量能够看出,这是一个二叉树,且极有可能就是一颗红黑树(由于有个black
)
经过新增一个kv对的调用链,来分析下这棵树,究竟是不是红黑树
将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; if (cpr != null) { // 下面这个循环能够得出树的左节点小于根小于右节点 // 而后找到新增的节点,做为叶子节点在树中的位置 // 注意这个相等时,直接更新了value值(这里表示插入一条已存在的记录) 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 { // 比较器不存在的逻辑,这时要求key继承 Comparable 接口 if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") 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); } // 建立一个Entry节点 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; }
从添加逻辑,能够得出结论:
因此新增一个kv对的逻辑就比较简单了
遍历树,将kv对做为叶子节点存在对应的位置
红黑树相关能够做为独立的一个知识点,这里不详细展开,基本上经过上面的分析,能够得出下面几个点
红黑值
相等扫一扫二维码,关注 小灰灰blog