一、继承与实现
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable
TreeMap<K,V>:TreeMap是以key-value形式存储数据的。
extends AbstractMap<K,V>:继承了AbstractMap,实现Map接口时需要实现的工作量大大减少了。
implements NavigableMap:实现了NavigableMap,可以返回特定条件最近匹配的导航方法。方法 lowerEntry、floorEntry、ceilingEntry 和 higherEntry 分别返回与小于、小于等于、大于等于、
大于给定键的键关联的 Map.Entry 对象,如果不存在这样的键,则返回 null。implements Cloneable:表明其可以调用clone()方法来返回实例的field-for-field拷贝。
implements Serializable:表明该类是可以序列化的。
二、类属性及说明
/**
* treeMap的排序规则,如果为null,则根据键的自然顺序进行排序
*/
private final Comparator<? super K> comparator; /** * 红黑数的根节点 */ private transient Entry<K,V> root; /** * 红黑树节点的个数 */ private transient int size = 0; /** * treeMap的结构性修改次数。实现fast-fail机制的关键。 */ private transient int modCount = 0; /** * 视图只有在第一次请求时创建并只创建一个 */ private transient EntrySet entrySet; private transient KeySet<K> navigableKeySet; private transient NavigableMap<K,V> descendingMap;
三、构造函数及说明
TreeMap():使用key的自然排序来构造一个空的treeMap。
TreeMap(Comparator<? super K> comparator):使用给定的比较器来构造一个空的treeMap。
TreeMap(Map<? extends K, ? extends V> m):使用key的自然排序来构造一个treeMap,treeMap包含给定map中所有的键值对
public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); }
TreeMap(SortedMap<K, ? extends V> m):使用指定的sortedMap来构造treeMap。treeMap中含有sortedMap中所有的键值对,键值对顺序和sortedMap中相同。使用一个有序的map构造新的treemap。这个方法需要线性时间
四、主要方法及说明
get方法:
/** * 通用二叉树的查找算法。提供log2(n)的复杂度 * @param key * @return */ final Entry<K,V> getEntry(Object key) { // Offload comparator-based version for sake of performance // 直接使用指定的比较器查找 if (comparator != null) return getEntryUsingComparator(key);// 与当前方法类似,只是使用compare(o1,o2)比较 // 不允许null 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); // key小于当前节点取左节点比较 if (cmp < 0) p = p.left; // key大于当前节点取右节点比较 else if (cmp > 0) p = p.right; // 找到 else return p; } // 没有找到 return null; } /** * 返回大于等于key的最小键。如果不存在就返回null。 * 算法: * 1.如果key < 当前节点p,寻找左子树,若左子树不存在,p就是最小大于key的 * 2.如果key > 当前节点p,寻找右子树,若右子树不存在,需要向上回溯第一个大于key的键 * 即key < 节点p,使用左链接(一个大于等于key的键的左子树中不存在更小的大于等于key的键) * @param key * @return */ final Entry<K,V> getCeilingEntry(K key) { Entry<K,V> p = root; while (p != null) { // 静态方法,比较器comparator为空就是用默认的比较 int cmp = compare(key, p.key); // key比当前节点小 if (cmp < 0) { // 当前节点存在更小的子节点 if (p.left != null) p = p.left; // 没有更小的子节点,当前节点就是树中最小大于key的节点 else return p; } // key比当前节点大 else if (cmp > 0) { // 当前节点存在更大的子节点 if (p.right != null) { p = p.right; } // 当前节点没有更大的节点能大于key,即子树中不存在大于key的键。只能向上回溯第一个使用左链接比较的节点 // 即当一个节点p大于key时,仍然在左子树中寻找小于p大于key的键,那么第一个左链接节点必定是最小大于key的 else { Entry<K,V> parent = p.parent; Entry<K,V> ch = p; // parent.left连接时退出循环或不存在大于等于key的键 while (parent != null && ch == parent.right) { ch = parent; parent = parent.parent; } return parent; } } // 找到key相等节点p else return p; } return null; } /** * 取小于等于key的最大键。遇到第一个key.compareTo(p) > 0的就会到右子树中取最小值 * 算法: * 1.如果key < 当前节点p,若p的左子树存在,就查找左子树,如果不存在,就回溯找第一个小于key的键 * 即k > 节点p,使用右连接的父节点(原理是当一个节点小于key,就会查找右子树是否有大一点的节点小于key) * 2.如果key > 当前节点p,寻找右子树,若右子树不存在就返回p最小大于等于key的 * @param key * @return */ final Entry<K,V> getFloorEntry(K key) { Entry<K,V> p = root; while (p != null) { int cmp = compare(key, p.key); // key比当前节点大 if (cmp > 0) { if (p.right != null) p = p.right; // 找不到比key更大的键,就返回当前键是小于key的最大键 else return p; } // key比当前节点小 else if (cmp < 0) { if (p.left != null) { p = p.left; } // 找不到 else { // 如果左子树中不存在跟接近key的节点就返回第一个右链接的父节点 Entry<K,V> parent = p.parent; Entry<K,V> ch = p; // 当parent使用右链接时退出循环或不存在小于等于key的键 while (parent != null && ch == parent.left) { ch = parent; parent = parent.parent; } return parent; } } // 相等 else return p; } return null; }
put方法:
/** * 在map中关联指定的键值对。不允许插入null。只对插入操作进行红黑树修复。提供log(n)复杂度 */ 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; // 使用比较器查找到key进行替换操作 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); } // 使用默认的顺序查找到key进行替换操作 else { // 不允许为null 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; } /** * 将指定映射中的所有映射关系复制到此映射中。如果已经存在的映射会被替换新的值 * 对于空treemap使用putAll提供线性时间。好于非空treemap的n*logn * @author Navy D */ public void putAll(Map<? extends K, ? extends V> map) { int mapSize = map.size(); // treemap是空的 参数map是sortedMap if (size==0 && mapSize!=0 && map instanceof SortedMap) { Comparator<?> c = ((SortedMap<?,?>)map).comparator(); // 比较器一样为null或同一个对象 if (c == comparator || (c != null && c.equals(comparator))) { ++modCount; try { // 使用迭代器通过key构建排序数据,需要线性时间 这个方法暂时没用到就不看了,用到再更新吧 buildFromSorted(mapSize, map.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } return; } } // abstractMap.putAll方法采用for调用put方法 super.putAll(map); }
delete方法:
/** * 在map中删除指定的键和关联的值 */ public V remove(Object key) { // 查找 Entry<K,V> p = getEntry(key); if (p == null) return null; V oldValue = p.value; // 删除键值对 deleteEntry(p); return oldValue; } /** * 删除节点p且平衡树 * 对于有两个子节点的删除节点p,使用右子树的最小节点替换, * 然后根据这个最小节点是叶子结点就先修复再删除,若有一个右子树就使用右子树中节点与父节点连接 * 对于有一个子节点的删除节点p,使用删除节点的子节点与父节点连接 * 对于没右子节点就修复后再删除 * @param p */ 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) { // 返回p的右子树中的最小节点 Entry<K,V> s = successor(p); // 将后继的键值覆盖删除点p的键值 没有更换颜色 p.key = s.key; p.value = s.value; // 将p引用为后继节点 注意不是覆盖到删除节点 删除点p已经被替换为后继 p = s; } // p has 2 children // Start fixup at replacement node, if it exists. // 如果删除节点只有一个孩子就返回这个孩子。注意如果进入上一个if,返回的p如果是叶节点,即replacement=null // p如果不是叶节点,只会存在右子节点 Entry<K,V> replacement = (p.left != null ? p.left : p.right); // 删除节点存在一个子节点(左或右),父节点链接删除节点的子节点 if (replacement != null) { // Link replacement to parent // 连接p的父节点与替换节点 删除p 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 // 只有删除点是BLACK的时候,才会触发删除修复,因为删除RED节点不会破坏红黑树的任何约束,而删除BLACK节点会导致有不同个数的黑色节点 if (p.color == BLACK) fixAfterDeletion(replacement); } // 只有root一个节点 else if (p.parent == null) { // return if we are the only node. root = null; } // 没有孩子节点 p是叶子结点 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; } } }
iterator方法: