java map 红黑树_Java 中TreeMap详解和红黑树

Java的一些组件很有趣,希望大家多深入研究研究!

A. 核心要点:

类继承

public class TreeMap

extends AbstractMap

implements NavigableMap, Cloneable, Serializable

一个有序的key-value集合,基于红黑树(Red-Black tree)实现。该映射根据其键的自然排序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。

此实现不是线程同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射(添加或者删除一个或多个映射),则其必须 外部同步。需要SortedMap m = Collections.synchronizedSortedMap(new TreeMap(…)); 来保证同步。

和其他Collections一样,iterator 方法返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出 ConcurrentModificationException。

B. 红黑树介绍

Treemap是基于红黑树实现的,其实红黑树的应用很是广泛。主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率高。

Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的

什么是红黑树?

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

性质1. 节点是红色或黑色。

性质2. 根节点是黑色。

性质3 每个叶节点(NIL节点,空节点)是黑色的。

性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

红黑树举例:

AAffA0nNPuCLAAAAAElFTkSuQmCC

C. Treemap部分源码分析。

对于一个map来说,插入,删除是经典动作,下面我们看一下Treemap的插入,删除源码。

插入, 下面以注释的方法来讲解

//put 就是往红黑树里面添加节点

public V put(K key, V value) {

Entry 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 parent;

// split comparator and comparable paths

// 下面这段代码是找到当前key-value,插入红黑树中的位置,treemap是以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();

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);

}

//此处,新建红黑树节点,key,value是待插入的值

Entry e = new Entry<>(key, value, parent);

if (cmp < 0)

parent.left = e;

else

parent.right = e;

//添加完节点以后,就可能不是一棵红黑树了,需要修正。

fixAfterInsertion(e);

size++;

modCount++;

return null;

}

/** From CLR */

//修正,保证插入完之后,还是一棵红黑树

private void fixAfterInsertion(Entry x) {

x.color = RED;

while (x != null && x != root && x.parent.color == RED) {

if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {

Entry y = rightOf(parentOf(parentOf(x)));

if (colorOf(y) == RED) {

setColor(parentOf(x), BLACK);

setColor(y, BLACK);

setColor(parentOf(parentOf(x)), RED);

x = parentOf(parentOf(x));

} else {

if (x == rightOf(parentOf(x))) {

x = parentOf(x);

rotateLeft(x);

}

setColor(parentOf(x), BLACK);

setColor(parentOf(parentOf(x)), RED);

rotateRight(parentOf(parentOf(x)));

}

} else {

Entry y = leftOf(parentOf(parentOf(x)));

if (colorOf(y) == RED) {

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;

}

删除, 下面以注释的方法来讲解

/**

* Delete node p, and then rebalance the tree.

*/

private void deleteEntry(Entry 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 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 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;

}

}

}

你可能感兴趣的:(java,map,红黑树)