文章目录
一. 回顾
前面了解了LinkedHashMap原理(浅谈),今天了解一下TreeMap。
二. 储备知识
在了解TreeMap前,了解以下两个知识比较容易理解:Comparable和Comparator;一致性hash
2.1 Comparable和Comparator
详情见Comparable和Comparator的知识点以及两者的区别
2.2 一致性hash
三. 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的优先级高