1. TreeMap是基于红黑树来实现的,它的特点是可排序,既可以按照键的自然顺序排序,也可以按照创建时指定的比较器进行排序。它的增删查改的时间复杂度为O(logn)。
2. 它的构造函数
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
/**
* The comparator used to maintain order in this tree map, or
* null if it uses the natural ordering of its keys.
*
* @serial
*/
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
/**
* The number of entries in the tree
*/
private transient int size = 0;
/**
* The number of structural modifications to the tree.
*/
private transient int modCount = 0;
/**
* Constructs a new, empty tree map, using the natural ordering of its
* keys. All keys inserted into the map must implement the {@link
* Comparable} interface. Furthermore, all such keys must be
* <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
* a {@code ClassCastException} for any keys {@code k1} and
* {@code k2} in the map. If the user attempts to put a key into the
* map that violates this constraint (for example, the user attempts to
* put a string key into a map whose keys are integers), the
* {@code put(Object key, Object value)} call will throw a
* {@code ClassCastException}.
*/
public TreeMap() {
comparator = null;
}
/**
* Constructs a new, empty tree map, ordered according to the given
* comparator. All keys inserted into the map must be <em>mutually
* comparable</em> by the given comparator: {@code comparator.compare(k1,
* k2)} must not throw a {@code ClassCastException} for any keys
* {@code k1} and {@code k2} in the map. If the user attempts to put
* a key into the map that violates this constraint, the {@code put(Object
* key, Object value)} call will throw a
* {@code ClassCastException}.
*
* @param comparator the comparator that will be used to order this map.
* If {@code null}, the {@linkplain Comparable natural
* ordering} of the keys will be used.
*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
/**
* Constructs a new tree map containing the same mappings as the given
* map, ordered according to the <em>natural ordering</em> of its keys.
* All keys inserted into the new map must implement the {@link
* Comparable} interface. Furthermore, all such keys must be
* <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
* a {@code ClassCastException} for any keys {@code k1} and
* {@code k2} in the map. This method runs in n*log(n) time.
*
* @param m the map whose mappings are to be placed in this map
* @throws ClassCastException if the keys in m are not {@link Comparable},
* or are not mutually comparable
* @throws NullPointerException if the specified map is null
*/
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
它的节点定义包含父节点,孩子节点,颜色,它的键,值。
//节点为树的内部类
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;
}
3. Put 方法
添加节点要根据比较器,找到插入的位置,调整父节点的指向,然后还可能需要调整红黑树。
public V put(K key, V value) {
Entry<K,V> t = root;
// 1. 如果根节点为空,把插入的节点作为根节点
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
//如果找到相同的key,设置新的value
return t.setValue(value);
} while (t != null);
}
else { //使用自然顺序比较器
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);
}
//创建节点e,把前面循环得到的当前的parent节点作为e的父节点
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;
}
4. remove方法
public V remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
//删除节点
deleteEntry(p);
//返回被删除节点的值
return oldValue;
}
deleteEntry() 删除节点, 如果待删除节点有孩子节点,用孩子节点去替代它的位置。如果被删除节点是黑色的话,要进行红黑树重新调整。
/**
* Delete node p, and then rebalance the tree.
*/
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.
//如果p有左右孩子,找到后继节点,把后继节点的值复制到节点P中,并让P指向后继节点
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
//把P的父节点作为替换点的父节点
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
//如果p为左孩子,那么替换点也作为p的父节点的左孩子
p.parent.left = replacement;
else
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
//把p节点的所有链接断开
p.left = p.right = p.parent = null;
// Fix replacement
// 如果P的颜色为黑色
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)
//把父节点的左孩子设为null
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
//把P的父节点设为null
p.parent = null;
}
}
}
5. 红黑树的特性
二叉查找树的左子树的所有节点小于根节点,右子树的所有节点大于根节点,它的左右子树也符合该性质。但是,当它的节点是只有左子树或者右子树的时候,就相当于是链表了。为了保持它的左右子树的平衡,设计了红黑树这种可以自动调整自身来保持平衡的二叉查找树的结构。它要满足以下的性质:
- 性质1:每个节点要么是黑色,要么是红色。
- 性质2:根节点是黑色。
- 性质3:每个叶子节点(NIL)是黑色。
- 性质4:每个红色结点的两个子结点一定都是黑色。
具体的使用,可以参考:https://www.jianshu.com/p/e136ec79235c
6. 总结
TreeMap可以用于基于排序的统计功能, 例如统计字符串的每个字符出现次数。对于增删改查以及统计的时间复杂度都控制在O(logn)的级别上。