【Java】之 TreeMap


一、简介


TreeMap 是按照 Key的排序结果来组织内部结构的 Map类集合,它改变了Map类散乱无序的形象。

TreeMap依靠ComparableComparator来实现Key的去重。

public class TreeMapRepeat {

    public static void main(String[] args) {

        // 如果仅此处的 TreeMap 换成 HashMap,则 size = 1
        TreeMap map = new TreeMap();
        map.put(new Key(), "value one");
        map.put(new Key(), "value two");

        // TreeMap, size = 2, 因为 Key 去重规则是根据排序结果
        System.out.println(map.size());
    }
}

class Key implements Comparable<Key> {

    // 返回负的常数,表示此对象永远小于收入的 other 对象,此处决定 TreeMap 的 size = 2
    @Override
    public int compareTo(Key other) {
        return -1;
    }

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

TreeMap是线程不安全的集合,不能在多线程之间进行共享数据的写操作。

在多线程进行写操作时,需要添加互斥机制,或者把对象放在Collections.synchroinzedMap(treeMap);中同步



二、源码分析


(1)属性

如图:
在这里插入图片描述

在这里插入图片描述


(2)put() 增加

插入新节点之前,需要明确的三个前提条件:

  1. 需要调整的新节点总是红色的
  2. 如果插入新节点的父节点是黑色的,无序调整。
  3. 如果插入新节点的父节点是红色的,因为红黑树规定不能出现相邻的两个红色节点,所以进入循环判断,或重新着色,或左右旋转,最终达到红黑树的五个约束条件,退出条件如下:
    while(x != null && x != root && x.parent.color == RED){...}

如图:
在这里插入图片描述

在这里插入图片描述


(3)fixAfterInsertion() 插入节点后动作

如图:
在这里插入图片描述
在这里插入图片描述


(4)remove() 删除

环境:JDK 8

public V remove(Object key) {
    // 在树中找到节点
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    // 删除节点
    deleteEntry(p);
    return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
    // 树的修改次数
    modCount++;
    
    // 节点数减少
    size--;
    
    // 若要删除的节点,左右子树不为null
    if (p.left != null && p.right != null) {
        // 找到继承者,然后简单值赋值
        Entry<K,V> s = successor(p);
        p.key = s.key;
        p.value = s.value;
        p = s; // 注意,现象 p = s
    } 

    // 寻找代替节点(类似继承者)
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);

    // 若不为 null
    if (replacement != null) {
        // Link replacement to parent
        replacement.parent = p.parent;
        
        // 若要删除的节点的父节点为null,即 p 为根节点,替代这变成根节点
        if (p.parent == null)
            root = replacement;
        // 找 p 的位置,是在父节点左边?
        else if (p == p.parent.left)
            p.parent.left  = replacement;
        else  // 找 p 的位置,实在父节点的右边?
            p.parent.right = replacement;

        // Null out links so they are OK to use by fixAfterDeletion.
        p.left = p.right = p.parent = null; // 解除依赖,方便GC回收

        // 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;
        }
    }
}

(5)rotateLeft() 左旋

如图:
在这里插入图片描述


(6)fixAfterDeletion() 删除节点后动作

fixAfterInsertion()fixAfterDeletion() 原理基本相同。

private void fixAfterDeletion(Entry<K,V> x) {
    while (x != root && colorOf(x) == BLACK) {  // 查询的节点,不是根节点且是黑色
        if (x == leftOf(parentOf(x))) { // x 是 其父节点的左儿子嘛?
            Entry<K,V> sib = rightOf(parentOf(x)); // 获取 x的父节点的右儿子

            // 如果右兄弟是红色
            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);       // 把右兄弟设置为黑色
                setColor(parentOf(x), RED); // 把父节点设置为红色
                rotateLeft(parentOf(x));    // 左旋转父节点
                sib = rightOf(parentOf(x)); // 获取父节点的右儿子
            }

            // 如果右兄弟的左儿子是黑色 且 右兄弟的右儿子是黑色
            if (colorOf(leftOf(sib))  == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) {
                setColor(sib, RED); // 把右兄弟设置为 红色
                x = parentOf(x);    // 把 x 设置为父节点
            } else {
                if (colorOf(rightOf(sib)) == BLACK) {
                    setColor(leftOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateRight(sib);
                    sib = rightOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(rightOf(sib), BLACK);
                rotateLeft(parentOf(x));
                x = root;
            }
        } else { // symmetric(对称)
            Entry<K,V> sib = leftOf(parentOf(x));

            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                rotateRight(parentOf(x));
                sib = leftOf(parentOf(x));
            }

            if (colorOf(rightOf(sib)) == BLACK &&
                    colorOf(leftOf(sib)) == BLACK) {
                setColor(sib, RED);
                x = parentOf(x);
            } else {
                if (colorOf(leftOf(sib)) == BLACK) {
                    setColor(rightOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateLeft(sib);
                    sib = leftOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(leftOf(sib), BLACK);
                rotateRight(parentOf(x));
                x = root;
            }
        }
    }

    setColor(x, BLACK);
}

(6)rotateRight() 右旋

private void rotateRight(Entry<K,V> p) {
    // 节点不为null
    if (p != null) {
        // 获取 p 的左子树
        Entry<K,V> l = p.left;
        // p的左指针 链接到 p 的左子树的右子树 
        p.left = l.right;
        if (l.right != null) l.right.parent = p; // 设置回溯链接
        // 设置共同的父节点
        l.parent = p.parent;
        
        // 主要让 l 代替 p的位置
        // 如果 p为 根节点
        if (p.parent == null)
            root = l;
        else if (p.parent.right == p)
            p.parent.right = l;
        else p.parent.left = l;
        l.right = p;
        p.parent = l;
    }
}

三、演示图

TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(55, "fifty-five");
treeMap.put(56, "fifty-six");
treeMap.put(57, "fifty-seven");
treeMap.put(58, "fifty-eight");
treeMap.put(83, "eighty-three");
treeMap.remove(57);
treeMap.put(59, "fifty-nine");

步骤如图:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

发布了404 篇原创文章 · 获赞 270 · 访问量 42万+

猜你喜欢

转载自blog.csdn.net/fanfan4569/article/details/101850192