java.util.TreeMap源码分析
Posted 没有梦想的小灰灰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java.util.TreeMap源码分析相关的知识,希望对你有一定的参考价值。
TreeMap的实现基于红黑树,排列的顺序根据key的大小,或者在创建时提供的比较器,取决于使用哪个构造器。
对于,containsKey,get,put,remove操作,保证时间复杂度为log(n)。
TreeMap的顺序与equals方法保持一致,这样才能遵守Map和SortMap的约定。
实现非同步,运行在多线程环境下,要进行外部同步,或者调用SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));方法得到一个同步化的TreeMap。
迭代器是快速失败的。
//AbstractMap实现Map接口,NavigableMap实现SortedMap接口,为了遵守约定,key的equals方法和客户提供的比较器或者key本身的compare结果要一致 public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable
实例变量:
//客户提供的比较器,如果不提供,为null private final Comparator<? super K> comparator; //TreeMap的根结点 private transient Entry<K,V> root = null; //TreeMap含有的结点数 private transient int size = 0; //TreeMap的修改次数,用于迭代器快速失败机制 private transient int modCount = 0;
TreeMap的红黑树结点实现
//结点颜色 private static final boolean RED = false; private static final boolean BLACK = true; //TreeMap的红黑树结点实现 static final class Entry<K,V> implements Map.Entry<K,V> { K key;//键 V value;//值 Entry<K,V> left = null;//左结点 Entry<K,V> right = null;//右结点 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; } }
4个构造器
//不提供比较器,使用key的自然顺序 public TreeMap() { comparator = null; } //提供比较器 public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } //使用m构造TreeMap,m没有比较器,所以还是使用key的自然顺序 public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); } //使用m构造TreeMap,m可能有,也可能没有比较器 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) { } }
put操作:
public V put(K key, V value) { Entry<K,V> t = root; //如果根节点为null 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) {//提供了比较器,使用比较器来比较 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的自然顺序,思想与上面一样 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<K,V> e = new Entry<>(key, value, parent); //根据比较值,设置新结点为父结点的左结点还是右结点 if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e);//在这之前的操作只是实现了排序二叉树,为了保持红黑色的性质,要对结点进行修正,左旋、右旋、着色. size++; modCount++; return null; }
插入后调整
private void fixAfterInsertion(Entry<K,V> x) { x.color = RED;//节点颜色设置为红色 //循环条件x不为空,且不是根节点且父节点颜色是红色,如果不是红色,那就没有连续的红色节点,不需要调整,如果x是根节点,不需要调整,设置为黑色即可 while (x != null && x != root && x.parent.color == RED) { //如果x的父节点是x的父节点的父节点的左节点 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //取x的父节点的父节点的右节点 Entry<K,V> y = rightOf(parentOf(parentOf(x))); //如果y的颜色是红色 if (colorOf(y) == RED) { //将x的父节点设置为黑色 setColor(parentOf(x), BLACK); //将y的颜色设置为黑色 setColor(y, BLACK); //将x的父结点的父节点设置为红色 setColor(parentOf(parentOf(x)), RED); //x的父节点的父节点的性质可能被破坏,递归地处理这个过程 x = parentOf(parentOf(x)); } else { //如果x是父节点的右节点 if (x == rightOf(parentOf(x))) { x = parentOf(x); //左旋 rotateLeft(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } else {//如果x的父节点是x父节点的父节点的右节点 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;//根节点始终为黑色 }
remove操作
public V remove(Object key) { //找出指定key的节点 Entry<K,V> p = getEntry(key); if (p == null) return null; V oldValue = p.value; deleteEntry(p);//删除节点 return oldValue;//返回被删除节点的值 } private void deleteEntry(Entry<K,V> p) { modCount++; size--; // If strictly internal, copy successor‘s element to p and then make p // point to successor. if (p.left != null && p.right != null) { Entry<K,V> s = successor(p); p.key = s.key; p.value = s.value; p = s; } // p has 2 children // Start fixup at replacement node, if it exists. Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) { // Link replacement to parent replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // Fix replacement if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { // return if we are the only node. root = null; } else { // No children. Use self as phantom replacement and unlink. if (p.color == BLACK) fixAfterDeletion(p);//删除节点后,为保证红黑树的性质,也需要进行调整 if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } }
get操作
public V get(Object key) { Entry<K,V> p = getEntry(key); return (p==null ? null : p.value); } final Entry<K,V> getEntry(Object key) { // Offload comparator-based version for sake of performance 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;//找不到,返回null } //寻找的思想是一样的,只是比较的时候用比较器 final Entry<K,V> getEntryUsingComparator(Object key) { @SuppressWarnings("unchecked") K k = (K) key; Comparator<? super K> cpr = comparator; if (cpr != null) { Entry<K,V> p = root; while (p != null) { int cmp = cpr.compare(k, p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } } return null; }
红黑数满足的5个性质:
性质 1:每个节点要么是红色,要么是黑色。
性质 2:根节点永远是黑色的。
性质 3:所有的叶节点都是空节点(即 null),并且是黑色的。
性质 4:每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)
性质 5:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。
插入或删除节点之后调整的目的都是为了满足上面的5个性质。
以上是关于java.util.TreeMap源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段