本期系列文章的其他文章
JDK1.8 Collection知识点与代码分析–整体框架
JDK1.8 Collection知识点与代码分析–ArrayList&LinkedList
JDK1.8 Collection知识点与代码分析–HashMap
JDK1.8 Collection知识点与代码分析–LinkedHashMap
JDK1.8 Collection知识点与代码分析–HashSet&TreeSet
这篇文章接着打开TreeMap的源码进行分析. 首先TreeMap是util工具类中一个非常好用的有序映射, 自身是非线程安全的, 相比HashMap, 增加了对NavigableMap, sortedMap接口的实现, 增加了一系列基于键有序的api, 这里先列举几个比较常用的
sortedMap
firstKey()
: 返回最小的keylastKey()
: 返回最大的keysubMap(from, to)
: 返回的是map的view, 左闭右开headMap(toKey, inclusive)
: 返回比toKey严格小(inclusive为true时包括)的这部分map的viewtailMap(fromKey, inclusive)
: 返回比fromKey严格大(inclusive为true时包括)的这部分map的viewNavigableMap(继承SortedMap)
ceilingEntry(key)/ ceilingKey(key)
: 返回大于等于key参数的最小的元素, 如果没有这样的元素存在返回nullfloorEntry(key)/floorKey(key)
: 返回小于等于key参数的最大元素, 如果没有这样的元素, 返回nullhigherEntry/higherKey
: 和ceiling类似, 只不过严格大lowerEntry/lowerKey
: 和floor类似, 只不过严格小TreeMap的核心数据结构是红黑树, 其红黑树的实现和HashMap
中的红黑树实现几乎是一模一样的, 只不过函数名和获取父节点, 左右节点的方法有微小的差别, 以下贴出核心部分的代码.
fixAfterInsertion
这个方法和HashMap
中的balanceInsertion
是一模一样的, 下面的注释看完没有弄懂的可以看下这篇讲hashMap的文章中的balanceInsertion
部分, 我在注释中通过图解的方式, 对每个部分解决的问题进行了梳理.
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
// x不是root节点, x的父节点为红节点
// x的父节点是祖先节点的左儿子
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
// flip Color
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
// x是右节点, x和父节点是红节点, 所以先左旋
x = parentOf(x);
rotateLeft(x);
}
// x, 父节点是左儿子, 也都是红节点, 右旋
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
//父节点是右儿子
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
//flip
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;
}
TreeMap中一个需要注意的细节, 当它没有指定comparator时, 是不允许null
作为key的, 使用comparator
时, 取决于comparator
是否允许key为null.
对于NavigatableMap
的api中的方法, 这里只给出一个例子, 它们的思路都非常相似
final Entry<K,V> getHigherEntry(K key) {
Entry<K,V> p = root;
while (p != null) {
int cmp = compare(key, p.key);
if (cmp < 0) {
// 如果当前节点比key大, 就继续往左找, 看是否能找到比当前更小的比key大的元素
if (p.left != null)
p = p.left;
else
return p;
} else {
if (p.right != null) {
p = p.right;
} else {
// 如果更大的没有了, 往父节点找, 找到第一个比key大的父节点(回溯,找到第一个向左走的位置)
Entry<K,V> parent = p.parent;
Entry<K,V> ch = p;
while (parent != null && ch == parent.right) {
ch = parent;
parent = parent.parent;
}
return parent;
}
}
}
return null;
}
这个方法的返回值并不是直接返回的, 而是通过exportEntry
方法, 构造了一个SimpleImmutableEntry
的实例进行发布, 保证安全性.
而对于SortedMap的api中方法, 它们返回的都是TreeMap的静态内部类AscendingSubMap
, 这个静态内部类又是继承于TreeMap
的抽象静态内部类NavigableSubMap
, 这个类的成员变量如下:
/**
* The backing map.
*/
final TreeMap<K,V> m;
/**
* Endpoints are represented as triples (fromStart, lo,
* loInclusive) and (toEnd, hi, hiInclusive). If fromStart is
* true, then the low (absolute) bound is the start of the
* backing map, and the other values are ignored. Otherwise,
* if loInclusive is true, lo is the inclusive bound, else lo
* is the exclusive bound. Similarly for the upper bound.
*/
final K lo, hi;
final boolean fromStart, toEnd;
final boolean loInclusive, hiInclusive;
可以看到, 它是成员TreeMap
的一个view, 内部维护了lo
,hi
两个key, 它的ceiling, floor等方法, 都是在检查是否在lo, hi范围后, 调用TreeMap
的getCeilingEntry
等方法.