一. 基本原理和优缺点
TreeMap与Hashmap、LinkedHashMap不同,他的底层不再是数组,而是一颗红黑树。在插入、删除或者替换元素时,TreeMap能按照事先约定的顺序来对key进行排序和迭代查询。
支持二叉搜索,因此做查询操作时,时间复杂度是O(logn),虽然比起纯粹使用数组要慢O(1),但是比普通的链表要快O(n)。插入数据类似链表,只调整几个指针就实现插入操作。
TreeMap的缺点在于,为了使用红黑树,每次新增、删除、修改一个节点后,都需要重新调整整颗树,达到红黑树的要求,这个调整的过程可能涉及到变色,也可能涉及到左旋、右旋,所以耗时啊!
二. 源码分析
2.1 put(K key, V value)
TreeMap<Integer, String> map = new TreeMap<>();
map.put(2, "张三");
map.put(1, "李四");
map.put(3, "王五");
map.put(4, "赵六");
Treemap默认使用key的升序排序,如果遍历上方的map,能获取到1->2->3->4排列顺序的key-value对。
我们也可以自定义比较key的方法,具体的做法如下:
Map<Integer, String> map = new TreeMap<Integer, String>(new Comparator<Integer> () {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}) {};
此时,再次遍历map,能获取到4->3->2->1排列顺序的key-value对。
我们可以把TreeMap put( )方法的源码分解成几个部分。
首先,判断当前TreeMap有没有节点,如果连一个节点都没有,那好办,就拿着本次待新增的k-v,做成一个节点,此时红黑树只有一个节点。
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;
}
接着,将待插入的key与根节点对应的key进行比较,这里就可以自定义比较方式了。
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();
@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);
}
上图中这么大一坨代码,无非就是列举了两种情况,如果没有显示的给出Comparator,则使用key的compareTo()方法比较大小。如果给出了显示的Comparator,则使用自定义的compare()方法进行比较。
然后,把较小的节点挂到根节点的左边,把较大的节点挂到根节点的右边。这不就是二叉搜索树的概念么。
if (cmp < 0)
parent.left = e;
else
parent.right = e;
最后,使用红黑树相关的算法,利用变色啊、旋转啊等手段,使添加了节点的二叉搜索树重新成为红黑树。
fixAfterInsertion(e);
2.2 红黑树节点的结构
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;