TreeMap源码解析

192 阅读3分钟

TreeMap底层数据结构说明

  • 对于数据完全随机的场景可以使用红黑树,只有极端情况会出现退化链表和不平衡树。
  • 查询性能最佳的是平衡二叉树
  • 红黑树的性能综合起来是最佳的
  • HashMap在java1.8以后引入了红黑树,TreeMap和TreeSet底层也是红黑树

红黑树案例

public class RBT <E extends Comparable<E>>{

    private static final boolean RED = true;
    private static final boolean BLACK = false;

    public Node root;
    private int size;

    private int size(){
        return size;
    }

    public void addEle(E e){
        root = addEle(root,e);
    }

    public Node addEle(Node node,E e){
        //第一次传入Node
        if(node==null){
            size++;
            return new Node(e);
        }

        // 第一次以后比较node和新添加的node大小 左小右大
        if(e.compareTo(node.e)<0){
            node.left = addEle(node.left,e);
        }else{
            node.right = addEle(node.right,e);
        }

        //如果节点的右子节点是红色 且左子节点不是红色
        if(isRed(node.right)&&!isRed(node.left)){
            node = leftRotate(node);
        }

        //如果节点的左子节点是红色 且左子节点的左子节点也是红色
        if(isRed(node.left)&&isRed(node.left.left)){
            node = rightRotate(node);
        }

        //颜色翻转
        if(isRed(node.right)&&isRed(node.left)){
            flipColors(node);
        }

        return node;
    }

    private void flipColors(Node node) {
        node.color = RED;
        node.left.color=BLACK;
        node.right.color = BLACK;
    }

    private Node rightRotate(Node node) {
        Node x = node.left;
        node.left = x.right;
        x.right = node;

        x.color = node.color;
        node.color = RED;

        return x;
    }

    private Node leftRotate(Node node) {
        Node x = node.right; //x为目标失衡节点
        node.right = x.left; // 将失衡节点的左节点摘除 接到失衡节点父节点的右边(左小右大)
        x.left = node; // 将node节点作为失衡节点的左节点
        //互换节点颜色,其实就是node节点和x节点父子关系发生了互换
        x.color = node.color;
        node.color=RED;
        return x;
    }

    private boolean isRed(Node node) {
       return node==null?BLACK:node.color;
    }


    private class Node {
        public E e;
        public Node left;
        public Node right;
        public boolean color;

        public Node(E e) {
            this.e = e;
            this.left = null;
            this.right = null;
            this.color = RED;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "e=" + e +
                    ", left=" + left +
                    ", right=" + right +
                    ", color=" + color +
                    '}';
        }
    }
}

使用while循环实现层次遍历树

public void LevelTraversal(){
    if(root==null){
        return;
    }
    Queue<Node> queue = new LinkedList<>();
    queue.add(root);
    while (!queue.isEmpty()){
        Node curNode = queue.remove(); // 调用的是 LinkedList的remove方法 删除第一个节点
        System.out.println(curNode);
        //判断子节点是否为null 子节点不为null就将它放入到队列中
        if(curNode.left!=null){
            queue.add(curNode.left);
        }
        if(curNode.right!=null){
            queue.add(curNode.right);
        }
    }
}

TreeMap源码 —— 验证底层是红黑树

TreeMap中有个静态内部类 Entry

static final class Entry<K,V> implements Map.Entry<K,V> { //Entry中存储的是key value 键值对
    K key;
    V value;
    Entry<K,V> left;
    Entry<K,V> right;
    Entry<K,V> parent;
    boolean color = BLACK;

    /**
     * Make a new cell with given key, value, and parent, and with
     * {@code null} child links, and BLACK color.
     */
    Entry(K key, V value, Entry<K,V> parent) {
        this.key = key;
        this.value = value;
        this.parent = parent;
    }

    /**
     * Returns the key.
     *
     * @return the key
     */
    public K getKey() {
        return key;
    }

    /**
     * Returns the value associated with the key.
     *
     * @return the value associated with the key
     */
    public V getValue() {
        return value;
    }

    /**
     * Replaces the value currently associated with the key with the given
     * value.
     *
     * @return the value associated with the key before this method was
     *         called
     */
    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;
    }
}

TreeMap的get方法

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
    // treeMap创建时是否创建了比较器 
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;//为了让key课比较转为Comparable
    Entry<K,V> p = root; // 先让P等于根节点
    while (p != null) {
        int cmp = k.compareTo(p.key); //根节点的key和当前key比较 左小右大 
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}

TreeMap的put方法

源码中用循环实现了递归,可以避免递归容易产生的栈溢出,这一点还是值得借鉴的

public V put(K key, V value) {
    Entry<K,V> t = root; //先拿到根节点
    if (t == null) { //根节点为null 首次添加
        compare(key, key); 

        root = new Entry<>(key, value, null); //创建一个新的节点
        size = 1; //节点个数
        modCount++; //计数器加1
        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
                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;
}

插入节点后维持平衡

private void fixAfterInsertion(Entry<K,V> x) {
    x.color = RED;
    //循环条件:新增节点不是null & 新增节点不是根节点 & 新增节点父节点是红色
    while (x != null && x != root && x.parent.color == RED) {
        //新增节点的父节点 是其父节点的上级节点的左孩子
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { 
            //得到父节点的上级节点的右孩子
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
             //其实这里完成了颜色翻转,如果y是红色 此时 x和y都是红色   
            if (colorOf(y) == RED) { 
                setColor(parentOf(x), BLACK);//因此xy应该设置为黑色
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);//x和y的上级节点设置为红色
                x = parentOf(parentOf(x));
            } else { //否则新增节点的上级节点的右孩子是黑色 就是x的父节点是红色 x的叔伯节点是黑色
                //如果节点添加在x父节点的右边 即 右不平衡 那么进行左旋
                if (x == rightOf(parentOf(x))) {
                    x = parentOf(x);
                    rotateLeft(x);
                }
                //让x父节点变为黑色
                setColor(parentOf(x), BLACK);
                //让x父节点的上级节点变为红色
                setColor(parentOf(parentOf(x)), RED);
                //让父节点的上级节点右旋
                rotateRight(parentOf(parentOf(x)));
            }
        } else {
            Entry<K,V> 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;
}