JDK1.8源码阅读(四):TreeMap

一、初识TreeMap

先看一下TreeMap的类图设计:
在这里插入图片描述

  • TreeMap 是一个 有序的key-value集合,它是通过 红黑树 实现的。
  • TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
  • TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法,比如返回有序的key集合。
  • TreeMap 实现了Cloneable接口,意味着它能被克隆。
  • TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。

TreeMap是基于红黑树实现的,红黑树是一种特殊的二叉排序树,红黑树通过一些限制,使其不会出现二叉树排序树中极端的一边倒的情况,相对二叉排序树而言,这自然提高了查询的效率。
在这里插入图片描述
红黑树的基本性质如下:

  • 每个节点都只能是红色或者黑色
  • 根节点是黑色
  • 每个叶节点(NIL节点,空节点)是黑色的
  • 如果一个结点是红的,则它两个子节点都是黑的,也就是说在一条路径上不能出现相邻的两个红色结点
  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

TreeMap排序默认是升序的,数字比较大小,字符串比较首字母,其他类型则需要自己实现Comparable接口,否则排序时会报错。

二、源码解析

1、成员变量

// key的比较器
private final Comparator<? super K> comparator;
// 根节点
private transient TreeMapEntry<K,V> root;
// 节点数量
private transient int size = 0;
// 修改的次数, fail-fast机制
private transient int modCount = 0;
// 键值对集合
private transient EntrySet entrySet;
// 键集合
private transient KeySet<K> navigableKeySet;
// 降序的NavigableMap
private transient NavigableMap<K,V> descendingMap;

2、构造函数

// 无参构造函数
public TreeMap() {
    comparator = null;
}
// 参数为比较器的构造函数
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}
// 参数为Map的构造函数
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}
// 参数为SortedMap的构造函数
public TreeMap(SortedMap<K, ? extends V> m) {
    comparator = m.comparator();
    try {
        buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
    } catch (java.io.IOException cannotHappen) {
    } catch (ClassNotFoundException cannotHappen) {
    }
}

3、put方法

public V put(K key, V value) {    
    Entry<K,V> t = root;    
    // 若红黑树为空,则插入根节点    
    if (t == null) {   
    	compare(key, key); 
        root = new Entry<K,V>(key, value, null);    
        size = 1;    
        modCount++;    
        return null;    
    }    
    int cmp;    
    Entry<K,V> parent;  
    // 比较器  
    Comparator<? super K> cpr = comparator;    
    // 红黑树是以key来进行排序的,以key来进行查找在二叉排序树中的插入位置。    
    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);    
    }    
    // 新建节点    
    Entry<K,V> e = new Entry<K,V>(key, value, parent);    
    if (cmp < 0)    
        parent.left = e;    
    else   
        parent.right = e;    
    // 插入新的节点后,调用fixAfterInsertion调整红黑树。    
    fixAfterInsertion(e);    
    size++;    
    modCount++;    
    return null;    
} 
 
/** 新增节点后对红黑树的调整 */
private void fixAfterInsertion(Entry<K,V> x) {
    // 将新插入节点的颜色设置为红色
    x. color = RED;

    // while循环,新插入节点x是根节点或者新插入节点x的父节点是红色这两种情况不需要调整
    while (x != null && x != root && x. parent.color == RED) {
        // 如果新插入节点x的父节点是祖父节点的左孩子
        if (parentOf(x) == leftOf(parentOf (parentOf(x)))) {
            // 取得新插入节点x的叔叔节点
            Entry<K,V> y = rightOf(parentOf (parentOf(x)));
            // 如果新插入x的父节点是红色-------------------①
            if (colorOf(y) == RED) {
                // 将x的父节点设置为黑色
                setColor(parentOf (x), BLACK);
                // 将x的叔叔节点设置为黑色
                setColor(y, BLACK);
                // 将x的祖父节点设置为红色
                setColor(parentOf (parentOf(x)), RED);
                // 将x指向祖父节点
                x = parentOf(parentOf (x));
            } else {
                // 如果新插入x的叔叔节点是黑色或缺少,且x的父节点是祖父节点的右孩子
                if (x == rightOf( parentOf(x))) {
                    // 左旋父节点
                    x = parentOf(x);
                    rotateLeft(x);
                }
                // 如果新插入x的叔叔节点是黑色或缺少,且x的父节点是祖父节点的左孩子
                // 将x的父节点设置为黑色
                setColor(parentOf (x), BLACK);
                // 将x的祖父节点设置为红色
                setColor(parentOf (parentOf(x)), RED);
                // 右旋x的祖父节点
                rotateRight( parentOf(parentOf (x)));
            }
        } else { 
        	// 如果新插入节点x的父节点是祖父节点的右孩子
            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;
}

/**
 * 对红黑树的节点(x)进行左旋转
 *
 * 左旋示意图(对节点x进行左旋):
 *      px                              px
 *     /                               /
 *    x                               y               
 *   /  \      --(左旋)--           / \                
 *  lx   y                          x  ry    
 *     /   \                       /  \
 *    ly   ry                     lx  ly 
 *
 */
private void rotateLeft(Entry<K,V> p) {
    if (p != null) {
        // 取得要选择节点p的右孩子
        Entry<K,V> r = p. right;
        // p的右孩子 = r的左孩子
        p. right = r.left ;
        // r的左孩子非空,p = r的左孩子的父亲
        if (r.left != null)
            r.left.parent = p;

        // 将p的父亲设为y的父亲
        r. parent = p.parent ;
        // 如果p的父亲是空节点,则将r设为根节点
        if (p.parent == null)
            root = r;
        // 如果p是它父节点的左孩子,则p的父节点的左孩子 = r
        else if (p.parent. left == p)
            p. parent.left = r;
        else             
            // 如果p是它父节点的左孩子,p的父节点的左孩子 = r
            p. parent.right = r;
        // r的左孩子 = p
        r. left = p;
        // p的父节点 = r
        p. parent = r;
    }
}


/**
 * 对红黑树的节点进行右旋转
 *
 * 右旋示意图(对节点y进行右旋):
 *            py                               py
 *           /                                /
 *          y                                x                 
 *         /  \      --(右旋)--            /  \                     
 *        x   ry                           lx   y 
 *       / \                                   / \                   
 *      lx  rx                                rx  ry
 *
 */
private void rotateRight(Entry<K,V> p) {
    if (p != null) {
        // 取得要选择节点p的左孩子
        Entry<K,V> l = p. left;           
        // 将"l的右孩子"设为"p的左孩子"
        p. left = l.right ;
        // 如果"l的右孩子"不为空的话,将"p"设为"l的右孩子的父亲"
        if (l.right != null) l. right.parent = p;
        // 将"p的父亲"设为"l的父亲"
        l. parent = p.parent ;
        // 如果"p的父亲"是空节点,则将l设为根节点
        if (p.parent == null)
            root = l;      
        // 如果p是它父节点的右孩子,则将l设为"p的父节点的右孩子"
        else if (p.parent. right == p)
            p. parent.right = l;
        //如果p是它父节点的左孩子,将l设为"p的父节点的左孩子"
        else p.parent .left = l;
        // 将"p"设为"l的右孩子"
        l. right = p;
        // 将"l"设为"p父节点"
        p. parent = l;
    }
}
  • 如果树为null,则构建一个TreeMapEntry设置为当前的root.
  • 检查之前TreeMap的comparator是否为空,不为空则用comparator去对比key,否则用k.compareTo(t.key)比较,然后遍历当前树,找到对应- 的key则修改对应的value。
  • 如果遍历树没找到,则通过new TreeMapEntry<>(key, value, parent); 添加到树上,然后执行fixAfterInsertion(e)保证root还是一颗红黑树。

4、remove方法

public V remove(Object key) {
    // 根据key查找到对应的节点对象
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    // 记录key对应的value
    V oldValue = p.value;
    // 删除节点
    deleteEntry(p);
    return oldValue;
}


private void deleteEntry(Entry<K,V> p) {
    modCount++;
    // map容器的元素个数减一
    size--;

    // 如果被删除的节点p的左孩子和右孩子都不为空,则查找其替代节点
    if (p.left != null && p. right != null) {
        // 查找p的替代节点
        Entry<K,V> s = successor (p);
        p. key = s.key ;
        p. value = s.value ;
        // 将p指向替代节点
        p = s;
    }

    // replacement为替代节点p的继承者,p的左孩子存在则用p的左孩子替代,否则用p的右孩子
    Entry<K,V> replacement = (p. left != null ? p.left : p. right);

    if (replacement != null) { 
        // 将p的父节点拷贝给替代节点
        replacement. parent = p.parent ;
        // 如果替代节点p的父节点为空,也就是p为跟节点,则将replacement设置为根节点
        if (p.parent == null)
            root = replacement;
        // 如果替代节点p是其父节点的左孩子,则将replacement设置为其父节点的左孩子
        else if (p == p.parent. left)
            p. parent.left   = replacement;
        // 如果替代节点p是其父节点的左孩子,则将replacement设置为其父节点的右孩子
        else
            p. parent.right = replacement;

        // 将替代节点p的left、right、parent的指针都指向空,即解除前后引用关系(相当于将p从树种摘除),使得gc可以回收
        p. left = p.right = p.parent = null;

        // Fix replacement
        // 如果替代节点p的颜色是黑色,则需要调整红黑树以保持其平衡
        if (p.color == BLACK)
            fixAfterDeletion(replacement);
    } else if (p.parent == null) {
        // 如果要替代节点p没有父节点,代表p为根节点,直接删除即可
        root = null;
    } else {
        // 判断进入这里说明替代节点p没有孩子
        // 如果p的颜色是黑色,则调整红黑树
        if (p.color == BLACK)
            fixAfterDeletion(p);
        // 下面删除替代节点p
        if (p.parent != null) {
            // 解除p的父节点对p的引用
            if (p == p.parent .left)
                p. parent.left = null;
            else if (p == p.parent. right)
                p. parent.right = null;
            // 解除p对p父节点的引用
            p. parent = null;
        }
    }
}

/**
 * 查找要删除节点的替代节点
 */
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
    if (t == null)
        return null;
    // 查找右子树的最左孩子
    else if (t.right != null) {
        Entry<K,V> p = t. right;
        while (p.left != null)
            p = p. left;
        return p;
    } else { 
    	// 查找左子树的最右孩子
        Entry<K,V> p = t. parent;
        Entry<K,V> ch = t;
        while (p != null && ch == p. right) {
            ch = p;
            p = p. parent;
        }
        return p;
    }
}

private void fixAfterDeletion(Entry<K,V> x) {
    // while循环,保证要删除节点x不是跟节点,并且是黑色
    while (x != root && colorOf (x) == BLACK) {
        // 如果要删除节点x是其父亲的左孩子
        if (x == leftOf( parentOf(x))) {
            // 取出要删除节点x的兄弟节点
            Entry<K,V> sib = rightOf(parentOf (x));

            // 如果删除节点x的兄弟节点是红色
            if (colorOf(sib) == RED) {
                // 将x的兄弟节点颜色设置为黑色
                setColor(sib, BLACK);
                // 将x的父节点颜色设置为红色
                setColor(parentOf (x), RED);
                // 左旋x的父节点
                rotateLeft( parentOf(x));
                // 将sib重新指向旋转后x的兄弟节点 ,进入else的步奏
                sib = rightOf(parentOf (x));
            }

            // 如果x的兄弟节点的两个孩子都是黑色
            if (colorOf(leftOf(sib))  == BLACK &&
                colorOf(rightOf (sib)) == BLACK) {
                // 将兄弟节点的颜色设置为红色
                setColor(sib, RED);
                // 将x的父节点指向x,如果x的父节点是黑色,需要将x的父节点整天看做一个节点继续调整
                x = parentOf(x);
            } else {
                // 如果x的兄弟节点右孩子是黑色,左孩子是红
                if (colorOf(rightOf(sib)) == BLACK) {
                    // 将x的兄弟节点的左孩子设置为黑色
                    setColor(leftOf (sib), BLACK);
                    // 将x的兄弟节点设置为红色
                    setColor(sib, RED);
                    // 右旋x的兄弟节点
                    rotateRight(sib);
                    // 将sib重新指向旋转后x的兄弟节点
                    sib = rightOf(parentOf (x));
                }
                // 如果x的兄弟节点右孩子是红色
                setColor(sib, colorOf (parentOf(x)));
                // 将x的父节点设置为黑色
                setColor(parentOf (x), BLACK);
                // 将x的兄弟节点的右孩子设置为黑色
                setColor(rightOf (sib), BLACK);
                // 左旋x的父节点
                rotateLeft( parentOf(x));
                // 达到平衡,将x指向root,退出循环
                x = root;
            }
        } else { 
        	// 如果要删除节点x是其父亲的右孩子
            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);
}

从红黑树中删除一个节点,需要调用fixAfterDeletion方法修复红黑树

5、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) {
    if (comparator != null)
        // 如果比较器为空,用key作为比较器查询
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
   Comparable<? super K> k = (Comparable<? super K>) key;
    // 取得root节点
    Entry<K,V> p = root;
    // 从root节点开始查找,根据比较器判断是在左子树还是右子树
    while (p != null) {
        int cmp = k.compareTo(p.key );
        if (cmp < 0)
            p = p. left;
        else if (cmp > 0)
            p = p. right;
        else
            return p;
    }
    return null;
}

final Entry<K,V> getEntryUsingComparator(Object key) {
   K k = (K) key;
    Comparator<? super K> cpr = comparator ;
    if (cpr != null) {
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = cpr.compare(k, p.key );
            if (cmp < 0)
                p = p. left;
            else if (cmp > 0)
                p = p. right;
            else
                return p;
        }
    }
    return null;
}

利用排序二叉树的特征来搜索key值对应的Entry,从二叉树的根节点开始,如果被搜索节点大于当前节点,程序向右子树搜索;如果被搜索节点小于当前节点,程序向左子树搜索;如果相等,那就是找到了指定节点。

三、总结

TreeMap内部用红黑树保存数据,迭代顺序按照key值有序,与HashMap相比效率更低,只建议在需要按序索引key值时使用,它也是非线程安全的,key和value均不能为null值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值