TreeMap原理(浅谈)

一. 回顾

前面了解了LinkedHashMap原理(浅谈),今天了解一下TreeMap。

二. 储备知识

在了解TreeMap前,了解以下两个知识比较容易理解:Comparable和Comparator;一致性hash

2.1 Comparable和Comparator

详情见Comparable和Comparator的知识点以及两者的区别

2.2 一致性hash

参考MemCache详细解读

三. TreeMap

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

可以看到TreeMap是继承AbstractMap,不像LinkedHashMap继承HashMap。而HashMap也是继承AbstractMap。


点进去看看NavigableMap,如下:

public interface NavigableMap<K,V> extends SortedMap<K,V> 

NavigableMap继承SortedMap,是对SortedMap的扩展,提供很多导航方法。(分析Comparable时,了解到了Comparable被运用于SortMap、SortSet中)


继续点进去看SortedMap,如下:

public interface SortedMap<K,V> extends Map<K,V> {

    /**
     * Returns the comparator used to order the keys in this map, or
     * {@code null} if this map uses the {@linkplain Comparable
     * natural ordering} of its keys.
     *
     * @return the comparator used to order the keys in this map,
     *         or {@code null} if this map uses the natural ordering
     *         of its keys
     */
    Comparator<? super K> comparator();

它继承Map。并且可以看到它有一个 成员方法方法是Comparator<? super K> comparator()。这就是用到Comparator的地方(如果没有Comparator,可以实现Comparable。后面详细讲述)。


SortedMap中还有两个很重要的方法,如下:
在这里插入图片描述


在TreeMap的implements notes注释中有一段,如下:

 * A Red-Black tree based {@link NavigableMap} implementation.
 * The map is sorted according to the {@linkplain Comparable natural
 * ordering} of its keys, or by a {@link Comparator} provided at map
 * creation time, depending on which constructor is used.

大概意思:这是基于红黑树实现的。TreeMap根据key的Comparable或者构造TreeMap时提供的Comparator进行排序。

总结:TreeMap的key对象必须实现Comparable接口或者构造TreeMap时提供一个Comparator

为了证明确实如此,作以下测试:

Person.java

package com.atguigu;

public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

  @Test
    public void test7() {
        TreeMap<Person, Integer> treemap = new TreeMap<>();
        treemap.put(new Person("tianqi", 7), 7);
        treemap.put(new Person("wangwu", 5), 5);
        treemap.put(new Person("zhaoliu", 6), 6);
        treemap.put(new Person("zhangsan", 3), 3);
        System.out.println(treemap);
    }

测试结果:
在这里插入图片描述
源码中的成员变量comparator也证实了要如此,如下:

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

注释的大概意思:comparator用来保持一个tree map中的排序。如果tree map的key有自然排序(即key实现了Comparable接口),那么compartor可以为null。

3.1 成员变量

comparator: comparator用来保持一个tree map中的排序。如果tree map的key有自然排序(即key实现了Comparable接口),那么compartor可以为null。

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

root: 根节点,其类型是TreeMap的静态内部类,后面会介绍到

private transient Entry<K,V> root;

size: tree中键值对的个数

/**
     * The number of entries in the tree
     */
    private transient int size = 0;

modCount : 修改次数(因为TreeMap是非同步的,即线程不安全的)

 /*Note that this implementation is not synchronized.*/

/**
     * The number of structural modifications to the tree.
     */
    private transient int modCount = 0;

entrySet、navigableKeySet、 descendingMap: 关于键值对的集合

 /**
     * Fields initialized to contain an instance of the entry set view
     * the first time this view is requested.  Views are stateless, so
     * there's no reason to create more than one.
     */
    private transient EntrySet entrySet;
    private transient KeySet<K> navigableKeySet;
    private transient NavigableMap<K,V> descendingMap;

3.2 静态常量

RED 、BLACK : 代表红黑结点

// Red-black mechanics

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

3.3 构造方法

有四个构造方法:
在这里插入图片描述

TreeMap() : 空参构造。注释中大概意思: 使用key的自然排序构造一个新的、空的tree map。tree map中所有的key必须实现Comparable接口。 所有的key能互相比较,否则抛出ClassCastException类型转换异常,比如tree map中全是Integer类型的key,现put进去一个String类型的key,put方法会抛出ClassCastException类型转换异常。

/**
     * Constructs a new, empty tree map, using the natural ordering of its
     * keys.  All keys inserted into the map must implement the {@link
     * Comparable} interface.  Furthermore, all such keys must be
     * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
     * a {@code ClassCastException} for any keys {@code k1} and
     * {@code k2} in the map.  If the user attempts to put a key into the
     * map that violates this constraint (for example, the user attempts to
     * put a string key into a map whose keys are integers), the
     * {@code put(Object key, Object value)} call will throw a
     * {@code ClassCastException}.
     */
    public TreeMap() {
        comparator = null;
    }

TreeMap(Comparator<? super K> comparator) : 指定comparator构造。若有指定compartor,可以没有comparable。put进去的键值对会根据comparator的排序规则对key进行排序从而进行存储。

/**
     * Constructs a new, empty tree map, ordered according to the given
     * comparator.  All keys inserted into the map must be <em>mutually
     * comparable</em> by the given comparator: {@code comparator.compare(k1,
     * k2)} must not throw a {@code ClassCastException} for any keys
     * {@code k1} and {@code k2} in the map.  If the user attempts to put
     * a key into the map that violates this constraint, the {@code put(Object
     * key, Object value)} call will throw a
     * {@code ClassCastException}.
     *
     * @param comparator the comparator that will be used to order this map.
     *        If {@code null}, the {@linkplain Comparable natural
     *        ordering} of the keys will be used.
     */
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

TreeMap(Map<? extends K, ? extends V> m): 入参为map,将map转为treemap。没有提供comparator,则默认为null,则使用key的自然排序(即key要实现Comparable接口)

 /**
     * Constructs a new tree map containing the same mappings as the given
     * map, ordered according to the <em>natural ordering</em> of its keys.
     * All keys inserted into the new map must implement the {@link
     * Comparable} interface.  Furthermore, all such keys must be
     * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
     * a {@code ClassCastException} for any keys {@code k1} and
     * {@code k2} in the map.  This method runs in n*log(n) time.
     *
     * @param  m the map whose mappings are to be placed in this map
     * @throws ClassCastException if the keys in m are not {@link Comparable},
     *         or are not mutually comparable
     * @throws NullPointerException if the specified map is null
     */
    public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
    }

TreeMap(SortedMap<K, ? extends V> m) : 入参为SortedMap,将SortedMap转为treemap。使用SortedMap的comparator。

 /**
     * Constructs a new tree map containing the same mappings and
     * using the same ordering as the specified sorted map.  This
     * method runs in linear time.
     *
     * @param  m the sorted map whose mappings are to be placed in this map,
     *         and whose comparator is to be used to sort this map
     * @throws NullPointerException if the specified map is null
     */
    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) {
        }
    }

总结:从上面四个构造方法了解到,如果不提供comparator,那么key就必须实现Comparable接口(即使用自然排序)。不了解这个自然排序的可以到Comparable和Comparator的知识点以及两者的区别了解

例子:

Person.java

package com.atguigu;

public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Main.java

  @Test
    public void test7() {
        TreeMap<Person, Integer> treemap = new TreeMap<>();
        treemap.put(new Person("tianqi", 7), 7);
        treemap.put(new Person("wangwu", 5), 5);
        treemap.put(new Person("zhaoliu", 6), 6);
        treemap.put(new Person("zhangsan", 3), 3);
        System.out.println(treemap);
    }

测试结果:
在这里插入图片描述

到这里,提了关于要实现Comparable或者Comparator超过2次了。那为什么要实现这个东西?我们还是从源码去分析,不能单凭猜测,得用事实说话。

3.4 静态内部类

Entry<K,V>: 其构造方法是有一个入参parent,是父节点的意思

/**
     * Node in the Tree.  Doubles as a means to pass key-value pairs back to
     * user (see Map.Entry).
     */

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

3.5 为什么要实现Comparable或者提供Comparator呢?

上面的报错信息来自tree map实例将元素put进去的时候,那么能说明是在调用put方法的时候报错了。我们点进去put方法看看。如下:

方法很长,我们一步步分析,首先看一下注释,大概意思是:将确定的值(即入参value)与确定的键(即入参key)关联起来(就是存储键值对的意思)。如果map中已经存在put进去的key,那么旧值会被新值覆盖。

再看到@throws给出的注释,大概意思是:如果入参key是null的,并且 这个map使用自然排序或者map的comparator不允许null的key,会抛出NullPointerException空指针异常。也就是说comparator可以允许null的key进行排序,comparable不允许null的key进行排序(详细原因见Comparable和Comparator的知识点以及两者的区别)。

/**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     *
     * @return the previous value associated with {@code key}, or
     *         {@code null} if there was no mapping for {@code key}.
     *         (A {@code null} return can also indicate that the map
     *         previously associated {@code null} with {@code key}.)
     * @throws ClassCastException if the specified key cannot be compared
     *         with the keys currently in the map
     * @throws NullPointerException if the specified key is null
     *         and this map uses natural ordering, or its comparator
     *         does not permit null keys
     */
    public V put(K key, V value) {
      
      ...
    }

使用上面的例子进行源码分析:
在这里插入图片描述
构造完就进行put元素,所以去看put方法是怎么实现的,如下:
在这里插入图片描述

调用compare()方法,如下:

在这里插入图片描述
假如构造的时候提供了comparator,那么三元运算符将会进行冒号后面的语句,如下:
在这里插入图片描述
总结:综上所述,这就为什么treemap的key要实现Comparable。因为刚开始构造treemap,里面无节点,所以无根节点,所以第一个put进去的元素是会作为根节点。也就在put进去时进行了key类型检查。key若没有实现Comparable会报错

继续看put方法,如下:
在这里插入图片描述
假如没有报错,put顺利完成,则会put进第二个元素,如下:

在这里插入图片描述
进入put()方法,如下:
在这里插入图片描述

在这里插入图片描述

四. 例子

4.1 需求:实现Comparable接口,使用treemap存值

User.java

package com.atguigu;

public class User implements Comparable<User>{
    private String name;
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(User o) {
        return (o.age-this.age);//按年龄的倒叙排
    }
}

Main.java

  @Test
    public void test7() {
        TreeMap<User, Integer> treemap = new TreeMap<>();
        treemap.put(new User("tianqi", 7), 7);
        treemap.put(new User("wangwu", 5), 5);
        treemap.put(new User("zhaoliu", 6), 6);
        treemap.put(new User("zhangsan", 3), 3);
        System.out.println(treemap);
    }

测试结果:存进去就是按照排序规则存的,排序规则是降序
在这里插入图片描述

4.2 需求:提供Comparator,使用treemap存值

Main.java

 @Test
    public void test8() {
        TreeMap<User, Integer> treemap = new TreeMap<>(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                if(o1.getAge() != null && o2.getAge() != null) {
                    return o1.getAge()-o2.getAge();//按年龄升序排序
                }else{
                    return o1.getAge()==null? 1: -1;//处理age为null的键值对,放在map的后面
                }
            }
        });
        treemap.put(new User("tianqi", 7), 7);
        treemap.put(new User("wangwu", null), null);
        treemap.put(new User("zhaoliu", 6), 6);
        treemap.put(new User("zhangsan", null), null);
        System.out.println(treemap);
    }

在这里插入图片描述
测试结果:Comparable是降序排序,Comparator是升序,测试结果是升序,说明Comparator比Comparable的优先级高
在这里插入图片描述

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TreeMap 是 Java 中的一种基于红黑树(Red-Black Tree)实现的有序映射(SortedMap)。下面是 TreeMap 的实现原理: 1. 数据结构: - TreeMap 内部使用红黑树作为底层数据结构,红黑树是一种自平衡二叉查找树。它通过在每个节点上增加一个存储位来表示节点的颜色(红色或黑色),并且在插入、删除等操作时通过变换来保持树的平衡性,从而保证了红黑树的高效性能和平衡性。 - TreeMap 中的每个节点都包含一个键值对(key-value pair),按照键的顺序进行排序。 2. 排序原理: - TreeMap 中的元素按照键的自然顺序或自定义比较器进行排序。如果键实现了 Comparable 接口,则按照键的自然顺序进行排序;否则,使用传入的 Comparator 进行排序。 - 在插入和删除元素时,通过红黑树的自平衡操作来保持树的平衡性,以维持有序性。 3. 时间复杂度: - TreeMap 的插入、删除和查询操作的平均时间复杂度为 O(log n),其中 n 是 TreeMap 中元素的个数。红黑树的平衡性保证了这些操作的高效性能。 4. 注意事项: - TreeMap 不允许键为 null,因为它需要根据键来进行排序和查找。 - 在使用自定义对象作为键时,需要确保对象实现了 Comparable 接口或提供了合适的 Comparator。 总结来说,TreeMap 使用红黑树作为底层数据结构来实现有序映射。通过红黑树的自平衡操作,TreeMap 保持了元素的有序性和高效的插入、删除和查询操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值