TreeMap底层的数据结构为红黑树,它不允许任何记录的键为null,但可以有多条记录的值为null。
红黑树的性质
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面的性质:
- 性质1:每个节点要么是黑色,要么是红色。
- 性质2:根节点是黑色。
- 性质3:每个叶子节点(NIL)是黑色。
- 性质4:每个红色结点的两个子结点一定都是黑色。
- 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
成员变量
1 | private final Comparator<? super K> comparator; |
存储结构
1 | static final class Entry<K,V> implements Map.Entry<K,V> { |
插入操作
put方法
put()方法的源码很长,但并不难理解。
首先,判断红黑树是否为空。若根结点为null,则创建根结点,方法到此结束;
然后,利用二叉排序树的性质,寻找插入的位置。若键已经存在,则改变对应的值,方法到此结束。
接着,创建新的结点,并将该结点插入到红黑树中;
最后,调整红黑树。
1 | public V put(K key, V value) { |
修复插入
插入操作可能会破坏红黑树的性质,因此插入以后需要执行修复操作。
首先,将结点x的颜色设置为红色。
然后,判断x的父结点P是否红色。若P为黑色,则不需要调整,方法结束。
接着,若P是其父结点G的左孩子:
1.若x的叔叔节点U是红色,则将P和U涂黑,G涂红。然后,将G作为当前节点,继续执行修复操作。
2.若x的叔叔节点U是黑色,此时祖父节点G必为黑色
- 2.1 若x是父节点P的右孩子,则对父节点P执行左旋操作,此时转变为2.2。
- 2.2 若x是父节点P的左孩子,则将父节点P涂黑,祖父节点G涂红,然后对祖父节点G执行右旋操作。
若P是其父结点G的右孩子,则刚好与上面相反。
最后,将根结点涂黑。
1 | private void fixAfterInsertion(Entry<K,V> x) { |
左旋操作
让当前节点p成为其右孩子r的左孩子,并让右孩子r原来的左孩子,成为p的右孩子。
1 | private void rotateLeft(Entry<K,V> p) { |
右旋操作
让当前节点p成为其左孩子l的右孩子,并让左孩子l原来的右孩子,成为p的左孩子。
1 | private void rotateRight(Entry<K,V> p) { |
删除操作
remove方法
1 | public V remove(Object key) { |
根据键名查找值
getEntry()方法比较简单,利用二叉查找树的性质,根据键名寻找对应的值。
1 | final Entry<K,V> getEntry(Object key) { |
删除结点
删除结点有三种情况:
- 该结点没有孩子;
- 该结点有一个孩子;
- 该结点有两个孩子。
1 | private void deleteEntry(Entry<K,V> p) { |
寻找后继结点
寻找当前结点的后继结点,即中序遍历顺序的下一个结点。(《剑指Offer》8.二叉树的下一个结点)
1 | static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) { |
修复删除
1 | private void fixAfterDeletion(Entry<K,V> x) { |