JavaSE(10)——TreeMap源码

TreeMap源码

1. 概述

1.1 描述

  • 底层是红黑树,实现了NavigableMap接口,可以根据Key自然排序,也可以在构造方法上传递Comparator实现Map的排序

  • 其中,containsKey、get、put、remove这些方法的时间复杂度是log(n)

  • 有序的Map中,一般是使用compareTo或者Compare方法来比较key,只要该两个方法认为相等,在Map的角度就认为这两个元素相等

  • TreeMap非同步

1.2 继承关系

在这里插入图片描述

1.3 特点

  • TreeMap实现了NavigableMap接口,而NavigableMap接口继承着SortedMap接口,致使TreeMap是有序的
  • TreeMap的底层是红黑树,时间复杂度为log(n)
  • 非同步
  • 使用Comparator或者COmparable来比较key是否相等与排序的问题

2. 源码

2.1 属性

//该比较器用来对TreeMap排序,如果为空,则按照自然顺序排序
private final Comparator<? super K> comparator;

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

//红黑树元素个数
private transient int size = 0;

//结构修改次数
private transient int modCount = 0;

2.2 构造方法

//默认使用自然排序
public TreeMap() {
    comparator = null;
}
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}

//指定了比较器
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}
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) {
    }
}

2.3 put方法

public V put(K key, V value) {
    //t为红黑树根节点
    Entry<K,V> t = root;

    //首次put时,红黑树还未初始化,
    if (t == null) {
        //类型可能为空检查
        compare(key, key);
        //创建新节点,作为根节点
        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }

    //cmp:比较器返回值
    int cmp;
    //parent:要插入的父节点处
    Entry<K,V> parent;
    // split comparator and comparable paths
    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
                //找到key值对应的节点,直接覆盖value值
                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
                //key值匹配的节点,直接覆盖value
                return t.setValue(value);
        } while (t != null);
    }

    //构建树节点,按cmp选择插入左分支还是右分支
    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;
}

2.4 get方法

//向外暴露的get方法
public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    
    //找到节点返回value,找不到节点返回null
    return (p==null ? null : p.value);
}

final Entry<K,V> getEntry(Object key) {
    // 当比较器不为空时,使用自定义的比较器寻找对应节点并返回
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();

    // 当比较器为空时,使用默认的比较器
    @SuppressWarnings("unchecked")
    Comparable<? super K> k = (Comparable<? super K>) key;

    //p为红黑树根节点,使用默认比较器找到key值相等的节点并返回
    Entry<K,V> p = 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;
}

2.5 remove方法

//删除指定节点
private void deleteEntry(Entry<K,V> p) {
    modCount++;
    size--;

    // If strictly internal, copy successor's element to p and then make p
    // point to successor.
    // 目标节点左右子树都不为空,
    if (p.left != null && p.right != null) {
        //寻找被删除节点的继承节点
        Entry<K,V> s = successor(p);
        //使用继承节点替换当前被删除节点
        p.key = s.key;
        p.value = s.value;
        p = s;
    } // p has 2 children

    // Start fixup at replacement node, if it exists.
    // 开始从被替换节点开始修复二叉树结构
    //当前被删除节点只有左子树或者右子树,拿到不为空的树进行修复
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);

    //左子树或右子树不为空
    if (replacement != null) {
        // Link replacement to parent
        // 将替换节点连接到被删除节点的父节点上
        replacement.parent = p.parent;
        if (p.parent == null)
            //如父节点为空,则设置为根节点
            root = replacement;
        //否则设置为父节点的左子节点或右子节点
        else if (p == p.parent.left)
            p.parent.left  = replacement;
        else
            p.parent.right = replacement;

        // Null out links so they are OK to use by fixAfterDeletion.
        // 空出无用的引用,以便删除修复函数使用
        p.left = p.right = p.parent = null;

        // 以被替换节点为值修复树结构
        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;
        }
    }
}

//寻找被删除节点的一下个节点
static <K,V> 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;
    }
}

2.6 遍历方法

abstract class PrivateEntryIterator<T> implements Iterator<T> {
    Entry<K,V> next;
    Entry<K,V> lastReturned;
    int expectedModCount;

    PrivateEntryIterator(Entry<K,V> first) {
        expectedModCount = modCount;
        lastReturned = null;
        next = first;
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Entry<K,V> nextEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        next = successor(e);
        lastReturned = e;
        return e;
    }

    final Entry<K,V> prevEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        next = predecessor(e);
        lastReturned = e;
        return e;
    }

    public void remove() {
        if (lastReturned == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // deleted entries are replaced by their successors
        if (lastReturned.left != null && lastReturned.right != null)
            next = lastReturned;
        deleteEntry(lastReturned);
        expectedModCount = modCount;
        lastReturned = null;
    }
}

successor(e)方法:

  • 返回目标节点的下一个节点,所谓下一个,就是按照次序排序之后的下一个。
  • 从代码中可以看出,如果右子树不为空,就返回右子树中最小的结点。如果右子树为空,则在左子树中寻找最大的结点。

3. 总结

  • TreeMap的底层是红黑树,能够实现该Map集合有序
  • 如果在构造方法中传递了Comparator对象,那么就会以Comparator对象的方法进行比较。否则,则使用Comparable的compareTo方法来比较
    • 如果使用的是compareTo方法来比较,key一定是不能为null,并且得实现了Comparable接口的。
    • 即使是传入了Comparator对象,不用compareTo方法来比较,key也是不能为null的
  • 源码中,Comparator和Comparable出现的频率是很高的,因为TreeMap实现有序要么就是外界传递进来Comparator对象,要么就使用默认key的Comparable接口 (实现自然排序)

要点:

  1. 由于底层是红黑树,那么时间复杂度可以保证为log(n)
  2. key不能为null,为null抛出NullPointException
  3. 想要自定义比较,在构造方法中传入Comparator对象,否则使用key的自然排序来进行比较
  4. TreeMap是非同步的,想要同步可以使用Collections来进行封装
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值