【Java源码分析】TreeMap源码分析

在这里插入图片描述

TreeMap

基于红黑树的NavigableMap实现。Map根据其键的自然顺序或Comparator在Map创建时提供的排序,具体取决于使用的构造函数。

TreeMap利用了红黑树左节点小,右节点大的性质,根据 key 进行排序,使每个元素能够插入到红黑树适当的位置,维护了 key 的大小关系,适用于 key 需要排序的场景。

因为底层使用的是红黑树,所以 TreeMap 中的 containsKey、get、put、remove 等方法的时间复杂度都是 log(n)。

实例

import java.util.*;
public class Test {
    public static void main(String[] args) {
        TreeMap<Integer,String> map=new TreeMap<>();
        map.put(3,"1");
        map.put(4,"4");
        map.get(1);
        map.remove(3);
        System.out.println(map.toString());

    }

}

基础类型和String都实现了比较接口

属性

   //比较器,如果外部有传进来Comparator比较器,首先用外部的
   private final Comparator<? super K> comparator;

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

    /**
     * 红黑树的已有元素个数
     */
    private transient int size = 0;

    /**
     * 树结构变化的版本号,用于迭代过程中的快速失败场景
     */
    private transient int modCount = 0;

节点

红黑树节点的结构

 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;
        ...
        
        }

构造函数

默认构造方法,需要key实现比较接口。

  public TreeMap() {
        comparator = null;
    }
  public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
  • Comparable:构造器不传递时候要求key必须实现Comparable接口
  • comparator:传入的comparator比较器比较两个key的大小,key不用实现比较接口
  • 如果以上两种情况都满足,优先使用外部的Comparator比较器

获取元素

 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)
            return getEntryUsingComparator(key);
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")//如果外部没有传递Comparator实例,将key强转为Comparable
            Comparable<? super K> k = (Comparable<? super K>) 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;
    }
 final Entry<K,V> getEntryUsingComparator(Object key) {
        @SuppressWarnings("unchecked")
            K k = (K) key;
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            Entry<K,V> p = root;
            while (p != null) {//根据comparator接口中的compare方法进行比较
                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;
    }

算法如下:

  • 从root开始遍历整个树
    如果待查找的key比当前遍历的key小,则在其左子树中查找
    如果待查找的key比当前遍历的key大,则在其右子树中查找
    如果待查找的key与当前遍历的key相等,则找到了该元素,直接返回
    如果遍历了整棵树也没有找到,则返回null

插入元素

插入元素,如果元素在树中存在,则替换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;//记录比较的结果值,大于0,小于0,等于0三种情况
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {//如果传入comparator比较器
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)//如果小于0从左子树寻找
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else//如果等于0,说明插入的节点已经存在了,直接更换其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
                    return t.setValue(value);
            } while (t != null);
        }//如果此节点在树中不存在,那么新建一个节点,并插入到树中
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)//如果小于0插入到左子节点
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);//插入节点之后做变色 + 旋转的操作使树平衡
        size++;
        modCount++;
        return null;
    }

总结

(1)TreeMap的存储结构只有一颗红黑树

(2)TreeMap中的元素是有序的,按key的顺序排列

(3)TreeMap比HashMap要慢一些,因为HashMap前面还做了一层桶,寻找元素要快很多

(4)TreeMap没有扩容的概念

猜你喜欢

转载自blog.csdn.net/qq_15604349/article/details/124455464