一. 回顧
前面瞭解了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的優先級高