为了账号安全,请及时绑定邮箱和手机立即绑定

JDK源码之TreeMap源码解析

标签:
Java

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

一个节点由key,value对(TreeMap),左子节点,右子节点,父亲节点,标志位color,红黑树只有2种颜色(red,black)组成。节点初始化的时候默认为Black。
树节点比较函数:

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

首先类型比较,然后key,value分别进行比较

再看看插入节点

public V put(K key, V value) {        Entry<K,V> 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<K,V> parent;        // split comparator and comparable paths        Comparator<? super K> cpr = comparator;        if (cpr != null) {            do {//根据key比较器,寻找节点的父亲节点,父亲节点一定是叶子节点                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);//说明该key已经存在,替换掉oldValue,并且返回oldValue            } while (t != null);        }        else {            if (key == null)                throw new NullPointerException();            @SuppressWarnings("unchecked")                Comparable<? super K> k = (Comparable<? super K>) key;//默认比较器            do {//根据key比较器,寻找节点的父亲节点,父亲节点一定是叶子节点                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;    }

可见,新节点一定是叶子节点,插入之后需要做出调整使其重新成为一颗红黑树,fixAfterInsertion如下所示:的作用是调整树使其重新平衡。我们知道,调整红黑树,无非就是左旋,右旋,改变颜色的各种组合。

fixAfterInsertion如下所示:

private void fixAfterInsertion(Entry<K,V> x) {        x.color = RED;//插入节点默认为红色,因为如果是黑色则违背红黑树从任意子树根节点出发到该子树任意叶子节点经历的黑节点数量必须相同,也就是所有的地方都可能需要调整,使其符合红黑树条件        while (x != null && x != root && x.parent.color == RED) {//如果插入节点不是根节点,而且插入节点的父亲节点是红节点,则违背不能连续有2个节点是红节点这个条件,需要做调整            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//如果插入节点的父亲节点是祖父节点的左孩子                Entry<K,V> y = rightOf(parentOf(parentOf(x)));//y 是插入节点的叔父节点                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<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;//根节点一定是黑色    }

左旋

/**     * 以p节点为中心逆时针左旋     * p的右孩子会成为新的父亲节点     * 旧父亲节点会成为新父亲节点的左孩子     * 旧右孩子的左孩子会成为新左孩子的右孩子     * From CLR     */    private void rotateLeft(Entry<K,V> p) {        if (p != null) {            Entry<K,V> r = p.right;            p.right = r.left;            if (r.left != null)                r.left.parent = p;            r.parent = p.parent;            if (p.parent == null)                root = r;            else if (p.parent.left == p)                p.parent.left = r;            else                p.parent.right = r;            r.left = p;            p.parent = r;        }    }

右旋:

/**     * 以p节点为中心顺时针右旋     * p的左孩子会成为新的父亲节点     * 旧父亲节点会成为新父亲节点的右孩子     * 旧左孩子的右孩子会成为新右孩子的左孩子     * From CLR     */    private void rotateRight(Entry<K,V> p) {        if (p != null) {            Entry<K,V> l = p.left;            p.left = l.right;            if (l.right != null) l.right.parent = p;            l.parent = p.parent;            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;        }    }

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消