【Java8源码分析】集合框架-TreeMap

时间:2022-06-07 14:41:33

一、红黑树原理

TreeMap是基于红黑树实现的。

一棵高度为h的二叉搜索树,它可以支持任何一种基本动态集合操作,其时间复杂度均为O(h)。当h较小时,执行会比较快。红黑树是许多“平衡”搜索树中的一种。


【Java8源码分析】集合框架-TreeMap

(1)性质

树中的结点有5个属性:color,key,key,left,right和p,满足以下五大性质:

  • 每个结点或是Red,或是Black
  • 根结点是Black的
  • 每个叶结点NIL是Black的
  • 如果一个结点是Red,则它的两个结点都是black
  • 对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的Black结点

(2)旋转

一棵含有n个关键字的红黑树上,搜索树操作插入和删除话费时间在O(lgn)。这两个操作对树做了修改,可能违反红黑性质,为维护这些性质,需改变树中结点颜色以及指针结构,一般通过旋转操作。


【Java8源码分析】集合框架-TreeMap

二、存储结构

    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;

Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}

public K getKey() {
return key;
}

public V getValue() {
return value;
}

public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}

public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;

return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}

public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}

public String toString() {
return key + "=" + value;
}
}

三、代码分析

(1)属性域

    // 比较器
private final Comparator<? super K> comparator;

// 根节点
private transient Entry<K,V> root;

private transient int size = 0;

// 实现fast-fail机制,跟ArrayList、LinkedList类似
private transient int modCount = 0;

(2)构造函数

    // 构造函数
public TreeMap() {
comparator = null;
}

// 带比较器
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}

public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
// 调用putAll把所有元素添加进去
putAll(m);
}

public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
// 此方法为从排序集合中构造TreeMap,复杂度为线性
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}

public void putAll(Map<? extends K, ? extends V> map) {
int mapSize = map.size();

// 如果TreeMap为空,且map为SortedMap
// 则调用从有序集合中构造TreeMap方法
if (size==0 && mapSize!=0 && map instanceof SortedMap) {
Comparator<?> c = ((SortedMap<?,?>)map).comparator();
// 如果比较器一样,才执行,否则调用putAll
if (c == comparator || (c != null && c.equals(comparator))) {
++modCount;
try {
// 调用从Sorted构造
buildFromSorted(mapSize, map.entrySet().iterator(),
null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
return;
}
}
// 其他情况执行以下代码
// 内部调用了put,put为TreeMap的添加元素函数
super.putAll(map);
}

// 从排序序列中构造TreeMap,线性时间
private void buildFromSorted(int size, Iterator<?> it,
java.io.ObjectInputStream str,
V defaultVal)
throws java.io.IOException, ClassNotFoundException {
this.size = size;
root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
it, str, defaultVal);
}

// 真正的从排序序列中构造TreeMap函数
@SuppressWarnings("unchecked")
private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
int redLevel,
Iterator<?> it,
java.io.ObjectInputStream str,
V defaultVal)
throws java.io.IOException, ClassNotFoundException {

// 策略:树的根节点肯定是排序序列的中间树
// 递归处理根节点的左树、右树
if (hi < lo) return null;

int mid = (lo + hi) >>> 1;

Entry<K,V> left = null;
if (lo < mid)
left = buildFromSorted(level+1, lo, mid - 1, redLevel,
it, str, defaultVal);

// extract key and/or value from iterator or stream
K key;
V value;
if (it != null) {
if (defaultVal==null) {
Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();
key = (K)entry.getKey();
value = (V)entry.getValue();
} else {
key = (K)it.next();
value = defaultVal;
}
} else { // use stream
key = (K) str.readObject();
value = (defaultVal != null ? defaultVal : (V) str.readObject());
}

Entry<K,V> middle = new Entry<>(key, value, null);

// color nodes in non-full bottommost level red
if (level == redLevel)
middle.color = RED;

if (left != null) {
middle.left = left;
left.parent = middle;
}

if (mid < hi) {
Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
it, str, defaultVal);
middle.right = right;
right.parent = middle;
}

return middle;
}

(3)查找

    final Entry<K,V> getEntry(Object key) {

if (comparator != null)
return getEntryUsingComparator(key);

// TreeMap的key值不能为null
if (key == null)
throw new NullPointerException();

// 如果未定义Comparator,则采用key的实现的Comparable
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
// 由根开始查找,小左大右
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}

// 带有Comparator比较器的查询,原理跟上面类似
final Entry<K,V> getEntryUsingComparator(Object key) {
@SuppressWarnings("unchecked")
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}

(4)添加

    public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
// 检查是否为null,再次提醒,key值不能为null
compare(key, key);

root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
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值相等,覆盖,返回旧值
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);
}
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;
}

(5)删除

    // 删除的函数比较复杂一些,主要需要考虑多种情况
// 具体说明可参考算法导论一书
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) {
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
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
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)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}

四、总结

  • TreeMap是根据key进行排序的,它的排序和定位需要依赖比较器或覆写Comparable接口,也因此不需要key覆写hashCode方法和equals方法,就可以排除掉重复的key,而HashMap的key则需要通过覆写hashCode方法和equals方法来确保没有重复的key
  • TreeMap的查询、插入、删除效率均没有HashMap高,一般只有要对key排序时才使用TreeMap
  • TreeMap的key不能为null,而HashMap的key可以为null

五、参考

http://blog.csdn.net/ns_code/article/details/36421085