TreeMap的整体架构
TreeMap 底层的数据结构是红黑树,和 HashMap 的红黑树结构一样。
不同的是,TreeMap 利用了红黑树左节点小,右节点大的性质,根据 key 进行排序,使每个元素能够插入到红黑树大小适当的位置,维护了 key 的大小关系,TreeMap 适用于 key 需要排序的场景。
因为底层的数据结构是红黑树,所以 containsKey()、get()、put()、remove() 等方法的时间复杂度都是 log(n)
TreeMap 的部分底层源码
public class TreeMap<K,V> extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable {// 用于在 TreeMap 中保持顺序的外部比较器,如果它使用键的自然顺序,则该值为 null// 如果该值为 null,则使用 key 实现的 Comparable.compareTo()private final Comparator<? super K> comparator;// 红黑树的根节点private transient Entry<K,V> root;// 红黑树节点的数量private transient int size = 0;// 树结构修改的次数private transient int modCount = 0;// 使用键的自然顺序构造一个新的空 TreeMap。// 键的类型必须实现 Comparable 接口// 所有的键相互比较 k1.compareTo(k2) 必须不抛出 ClassCastExceptionpublic TreeMap() {comparator = null;}// 根据给定的外部排序器构造一个新的空 TreeMap// 所有键相互比较由给定的比较器 comparator.compare(k1, k2) 必须不抛出 ClassCastExceptionpublic TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}// 构造一个包含给定 map 数据的 TreeMap// 根据键的自然顺序排序// 键的类型必须实现 Comparable 接口// 所有的键相互比较 k1.compareTo(k2) 必须不抛出 ClassCastException// 该方法运行时间为 n*log(n)public TreeMap(Map<? extends K, ? extends V> m) {comparator = null;putAll(m);}// 构造一个包含给定 map 数据的 TreeMap,并使用与指定的 SortedMap 相同的顺序// 该方法在线性时间内运行public TreeMap(SortedMap<K, ? extends V> m) {comparator = m.comparator();try {buildFromSorted(m.size(), m.entrySet().iterator(), null, null);} catch (java.io.IOException cannotHappen) {} catch (ClassNotFoundException cannotHappen) {}}// Red-black mechanicsprivate 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;Entry(K key, V value, Entry<K,V> parent) {this.key = key;this.value = value;this.parent = parent;}public K getKey() {return key;}public V getValue() {return value;}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;}}
TreeMap的新增
TreeMap 新增节点的大概步骤:
- 判断红黑树的跟节点是否为空,为空的话,新增的节点直接作为根节点
- 根据红黑树左小右大的特性,进行判断,找到应该新增节点的父节点,或者直接替换旧值
- 在父节点的左边或右边插入新增节点
- 着色旋转,达到平衡,结束
put() 的底层源码实现如下:
// 将指定值与此 TreeMap 中的指定键关联。如果之前的 TreeMap 包含键的映射,那么旧的值将被替换// 返回 key 之前映射的值,如果不存在则返回 nullpublic V put(K key, V value) {// 红黑树的根节点Entry<K,V> t = root;// 判断红黑树的跟节点是否为空,如果为 null,则新增节点直接作为根节点if (t == null) {// 使用该 TreeMap 的正确比较方法比较两个键// compare 方法限制了 key 不能为 null(但 val 可以为 null)compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}//cmp 用于存储每一次节点的 key 对比的结果int cmp;// 应该新增节点的父节点Entry<K,V> parent;// split comparator and comparable pathsComparator<? super K> cpr = comparator;// 如果传入的外部排序器 cpmparator 不为 null,则使用外部排序器if (cpr != null) {do {// 第一次循环时,parent 是红黑树的根节点,一次循环结束时,parent 就是上次比过的对象parent = t;cmp = cpr.compare(key, t.key);// 如果新增节点的 key < 当前节点的 keyif (cmp < 0)t = t.left;// 如果新增节点的 key > 当前节点的 keyelse if (cmp > 0)t = t.right;// 如果新增节点的 key == 当前节点的 key,不用新增节点,直接替换旧值elsereturn t.setValue(value);} while (t != null);}// 如果传入的外部排序器 cpmparator 为 null,则使用 key 实现的 Comparable.compareTo()else {if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;// 这个循环和上面 if 中的循环类似,不做注释说明do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}// 构建新增节点Entry<K,V> e = new Entry<>(key, value, parent);// 此时 cmp 代表最后一次对比的结果// 如果 cmp < 0 ,代表节点 e 的 key < 节点 parent 的 keyif (cmp < 0)// 节点 e 作为节点 parent 的左子节点parent.left = e;elseparent.right = e;// 进行着色旋转操作fixAfterInsertion(e);// 红黑树的节点数量 + 1size++;// 红黑树的结构的修改次数 + 1modCount++;return null;}// 使用该 TreeMap 的正确比较方法比较两个键final int compare(Object k1, Object k2) {return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2): comparator.compare((K)k1, (K)k2);}/** From CLR */// 该方法进行着色旋转操作private void fixAfterInsertion(Entry<K,V> x) {x.color = RED;while (x != null && x != root && x.parent.color == RED) {if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {Entry<K,V> y = rightOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == rightOf(parentOf(x))) {x = parentOf(x);rotateLeft(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateRight(parentOf(parentOf(x)));}} else {Entry<K,V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {setColor(parentOf(x), BLACK);setColor(y, BLACK);setColor(parentOf(parentOf(x)), RED);x = parentOf(parentOf(x));} else {if (x == leftOf(parentOf(x))) {x = parentOf(x);rotateRight(x);}setColor(parentOf(x), BLACK);setColor(parentOf(parentOf(x)), RED);rotateLeft(parentOf(parentOf(x)));}}}root.color = BLACK;}
