1、概述
TreeMap是Java内部实现比较复杂的集合类之一。与HashMap不一样,TreeMap的底层不是用哈希表实现的,而是用红黑树实现的。另外,HashMap存取元素的时间复杂度是O(1)的常量级,而TreeMap对元素的操作复杂度为O(log n)。虽然在操作性能方面,TreeMap不占优势,但是因为它使用红黑树(平衡二叉查找树)实现,所以它内部的元素都是排好序的。当需要查找的元素是排好序的,TreeMap的优势就体现出来了。
2、红黑树简介
首先,介绍一下红黑树的基本性质。红黑树不是一棵严格意义上的平衡树,因为它的高度差会大于1。了解一下它的性质就清楚了。
红黑树是一棵二叉树,它满足以下5个特性:
<1>每个节点的颜色是红色或黑色。
<2>根节点必须是黑色的。
<3>每个叶节点是黑色的(叶节点是指树尾端的NULL节点)。
<4>如果一个节点是红色的,那么它的子节点必须是黑色。即,不能有连续的红色节点。
<5>对于任意一个节点,从它到叶节点的每条路径包含相同数量的黑色节点。
由上述定义可知,红黑树是一棵相对平衡的二叉查找树。因为红黑树天生需要平衡,所以就可以避免一般的二叉查找树在极端情况(插入的数据已经排好序)失去平衡的情况。
3、TreeMap的Entry类
由于TreeMap包含了tree和Map的性质,它所包含的的Entry类应该有以下6个成员变量:
左子结点引用、右子节点引用、父节点引用、key、value、color。
在JDK中,Entry的具体定义如下:
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;
}
}
4、TreeMap的put(key ,value)操作
插入元素由put操作进行,主要包括两个过程,第一步是通过计算key找到插入元素的位置,如果新加入的key不存在直接插入;如果存在,那么就更新原节点的值。第二步是,因为插入新元素可能会破坏红黑树的属性,如果被破坏了,就要通过左右旋转等操作恢复红黑树的属性。具体代码如下:
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) {
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 {
if (key == null)
throw new NullPointerException();
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;
}