深入浅出源码探究TreeMap

Map接口

Map集合的特点

  1. 能够存储唯一的列的数据(唯一,不可重复) Set
  2. 能够存储可以重复的数据(可重复) List
  3. 值的顺序取决于键的顺序
  4. 键和值都是可以存储null元素的

Set接口

一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对
e1e2,并且最多包含一个 null 元素

在这里插入图片描述

TreeMap

  TreeMap底层的实现原理是红黑树,所以要搞清楚TreeMap的底层原理,前提条件就必须要搞清楚红黑树的原理

红黑树原理

类图结构
在这里插入图片描述
在这里插入图片描述
定义TreeMap 无参构造

TreeMap map = new TreeMap();

源码

 public TreeMap() {
        comparator = null;
    }
public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
    /**
     * The comparator used to maintain order in this tree map, or
     * null if it uses the natural ordering of its keys.
     *
     * @serial
     */
    private final Comparator<? super K> comparator;// 比较器

    private transient Entry<K,V> root;// 根节点

    /**
     * The number of entries in the tree
     */
    private transient int size = 0;// map中元素的个数

    /**
     * The number of structural modifications to the tree.
     */
    private transient int modCount = 0;// 记录修改的次数

进入Entry内部类

static final class Entry<K,V> implements Map.Entry<K,V> {
    K key; // key
    V value; // 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;
        // 重写了equals方法 必须是key和value都相等
        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;
    }
}

以put方法为例来介绍TreeMap中红黑树的操作

TreeMap map = new TreeMap();
map.put("wm","666");
map.put("wmm","777");

第一次添加

public V put(K key, V value) {
    // 获取根节点 将root赋值给局部变量 初始为Null
    Entry<K,V> t = root;
    if (t == null) {
        // 初始操作
        // 检查key是否为null
        compare(key, key); // type (and possibly null) check
        // 将要添加的key、value封装为一个Entry对象 并赋值给root
        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null; //返回null
    }

第二次添加 root不为空

public V put(K key, V value) {
    // 获取根节点 将root赋值给局部变量 初始为Null
    Entry<K,V> t = root;
    if (t == null) {
        // 初始操作
        // 检查key是否为null
        compare(key, key); // type (and possibly null) check
        // 对根节点初始化
        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null; //返回null
    }
    int cmp;
    Entry<K,V> parent;//父节点
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;//获取比较器
    if (cpr != null) { // 如果比较器不为null
        do { // 循环 将root赋值给parent
            parent = t;
            // 比较父节点和插入节点的值得大小
            cmp = cpr.compare(key, t.key);
            if (cmp < 0) // 插入节点比父节点小
                t = t.left; // 把父节点左节点赋给t
            else if (cmp > 0) // 插入的值比父节点大
                t = t.right; // 把父节点右侧的节点赋给t
            else // 如果相等,直接替换,并返回原来的值
                return t.setValue(value);
        } while (t != null);// 一直循环直到找到合适的插入位置
    }
    else {
        if (key == null)
            throw new NullPointerException();
        // 比较器为空 就创建一个 通过ASCII码值进行比较
        @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);
    }
	// 将要添加的 key  value 封装为一个Entry 对象
	// t 就是我们要插入节点的父节点 parent
    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(e);

/** From CLR */
private void fixAfterInsertion(Entry<K,V> x) {
    // 设置创建的初始节点为红色节点 
	x.color = RED;
	// 循环的条件 添加的节点不为空 不是root节点 父节点是红色
    while (x != null && x != root && x.parent.color == RED) {
		// 判断父节点是否是祖父节点的左节点
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
			// 获取父节点的兄弟节点
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
			// 父节点的兄弟节点是红色
            if (colorOf(y) == RED) {
			// 设置父节点为黑色
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK); // 设置父节点的兄弟节点也为黑色
                setColor(parentOf(parentOf(x)), RED); // 设置祖父节点为红色
				// 把祖父节点赋给x 因为祖父节点是红色,当前新插入的节点 下一次循环向上再检查
                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; // 根节点必须为黑色
}

左旋源码

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

右旋源码

/** 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;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值