一、前言

  前面所说的hashMap和linkedHashMap都不具备统计的功能,或者说它们的统计性能的时间复杂度都不是很好,要想对两者进行统计,需要遍历所有的entry,时间复杂度比较高,此时,我们就需要使用treeMap。

  treeMap的key按照自然顺序进行排序或根据创建时提供的comparator接口进行排序,TreeMap为增、删、改、查这些操作提供了log(N)的时间开销,从存储角度而言,这比HashMap与LinkedHashMap的O(1)时间复杂度要差些;但是在统计性能上,TreeMap同样可以保证log(N)的时间开销,这又比HashMap与LinkedHashMap的O(N)时间复杂度好不少。

  因此,如果只需要存储功能,使用hashMap或linkedHashMap是一种更好的选择;如果还需要保证统计功能或是对key按照一定的规则进行排序,那么使用treeMap是一种更好的选择。

  四个关注点在treeMap上的答案

二、treeMap的数据结构

  treeMap底层使用的数据结构是红黑树,下图为典型的红黑树结构,效率很高。

  用来存储key-value的是treeMap中的Entry类,除了key-value属性,还有该节点的左节点left,右节点right,父节点parent。

三、treeMap源码分析-属性及构造函数

  3.1 类的继承关系

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

  说明:继承了抽象类AbstractMap,AbstractMap实现了Map接口,实现了部分方法。实现了NavigableMap,Cloneable,Serializable接口,其中NavigableMap是继承自SortedMap的接口,定义了一系列规范。

  3.2 类的属性

/**
* The comparator used to maintain order in this tree map, or
* null if it uses the natural ordering of its keys.
* 比较器,用于维持treeMap中key的顺序,为null时,使用的是key的自然顺序
* @serial
*/
private final Comparator<? super K> comparator;
//树的根节点
private transient Entry<K,V> root; /**
* The number of entries in the tree  树中节点的数量
*/
private transient int size = 0; /**
* The number of structural modifications to the tree.  对树进行结构性修改的次数
*/
private transient int modCount = 0;

  3.3 类的构造函数

  1、TreeMap()型

/**
* 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,排序规则是key的自然顺序,所有要存储的元素的key必须实现Comparable接口,而且,这些key要可以互相比较,否则会抛ClassCastException,即类型转换异常。

  举例:正常情况下,key实现了Comparable接口

public class Test {
public static void main(String[] args) {
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put("a","aaa");
treeMap.put("d","ddd");
treeMap.put("b","bbb");
treeMap.put("c","ccc"); Set<Map.Entry<String, String>> entries = treeMap.entrySet();
for(Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey() + "========" + entry.getValue());
} }
}

  结果:

a========aaa
b========bbb
c========ccc
d========ddd

  说明:可以看到,treeMap中已经根据key的自然顺序进行排序了,key所属类型String类实现了Comparable接口

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence

  key所属String类下的比较规则:

/**
* Compares two strings lexicographically.
* The comparison is based on the Unicode value of each character in
* the strings. The character sequence represented by this
* {@code String} object is compared lexicographically to the
* character sequence represented by the argument string. The result is
* a negative integer if this {@code String} object
* lexicographically precedes the argument string. The result is a
* positive integer if this {@code String} object lexicographically
* follows the argument string. The result is zero if the strings
* are equal; {@code compareTo} returns {@code 0} exactly when
* the {@link #equals(Object)} method would return {@code true}.
* <p>
* This is the definition of lexicographic ordering. If two strings are
* different, then either they have different characters at some index
* that is a valid index for both strings, or their lengths are different,
* or both. If they have different characters at one or more index
* positions, let <i>k</i> be the smallest such index; then the string
* whose character at position <i>k</i> has the smaller value, as
* determined by using the &lt; operator, lexicographically precedes the
* other string. In this case, {@code compareTo} returns the
* difference of the two character values at position {@code k} in
* the two string -- that is, the value:
* <blockquote><pre>
* this.charAt(k)-anotherString.charAt(k)
* </pre></blockquote>
* If there is no index position at which they differ, then the shorter
* string lexicographically precedes the longer string. In this case,
* {@code compareTo} returns the difference of the lengths of the
* strings -- that is, the value:
* <blockquote><pre>
* this.length()-anotherString.length()
* </pre></blockquote>
*
* @param anotherString the {@code String} to be compared.
* @return the value {@code 0} if the argument string is equal to
* this string; a value less than {@code 0} if this string
* is lexicographically less than the string argument; and a
* value greater than {@code 0} if this string is
* lexicographically greater than the string argument.
*/
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value; int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}

  第二种情况:定义一个Student类当做treeMap的key,Student类并未实现Comparable接口

public class Student{

    private String name;
private String age; public Student() {
} public Student(String name, String age) { this.name = name;
this.age = age;
} @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getAge() {
return age;
} public void setAge(String age) {
this.age = age;
}
}

  测试一下:

public class Test {
public static void main(String[] args) {
TreeMap<Student, String> treeMap = new TreeMap<>();
Student student1 = new Student("zs", "10");
Student student2 = new Student("ls", "12");
Student student3 = new Student("ww", "11");
Student student4 = new Student("zl", "15");
treeMap.put(student1,"beijing");
treeMap.put(student2,"shenzhen");
treeMap.put(student3,"hangzhou");
treeMap.put(student4,"guangzhou"); Set<Map.Entry<Student, String>> entries = treeMap.entrySet();
for(Map.Entry<Student, String> entry : entries) {
System.out.println(entry.getKey() + "==========" + entry.getValue());
}
}
}

  结果:

Exception in thread "main" java.lang.ClassCastException: com.dajia.test.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at com.dajia.test.Test.main(Test.java:23)

  说明:可以看到,当key未实现Comparable接口时,在put元素的时候就会报错。

  当Student类实现Comparable接口时:比较规则是按照age的大小进行比较

public class Student implements Comparable<Student>{

    private String name;
private String age; public Student() {
} public Student(String name, String age) { this.name = name;
this.age = age;
} @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getAge() {
return age;
} public void setAge(String age) {
this.age = age;
} @Override
public int compareTo(Student o) {
return this.age.compareTo(o.age);
}
}

  相同的测试代码测试一下,结果:按照key(student)的排序规则进行了排序

Student{name='zs', age='10'}==========beijing
Student{name='ww', age='11'}==========hangzhou
Student{name='ls', age='12'}==========shenzhen
Student{name='zl', age='15'}==========guangzhou

  2、TreeMap(Comparator<? super K> comparator)型

/**
* 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,参数是自定义的比较器,排序规则是该比较器的比较规则,这些key要可以互相比较,否则会抛ClassCastException,即类型转换异常。

  举例:自定义一个比较器StudentComparator,比较student下的age大小

public class StudentComparator implements Comparator<Student>{

    @Override
public int compare(Student o1, Student o2) {
return o1.getAge().compareTo(o2.getAge());
}
}

  测试一下:

public class Test {
public static void main(String[] args) {
StudentComparator studentComparator = new StudentComparator();
TreeMap<Student, String> treeMap = new TreeMap<>(studentComparator);
Student student1 = new Student("zs", "10");
Student student2 = new Student("ls", "12");
Student student3 = new Student("ww", "11");
Student student4 = new Student("zl", "15");
treeMap.put(student1,"beijing");
treeMap.put(student2,"shenzhen");
treeMap.put(student3,"hangzhou");
treeMap.put(student4,"guangzhou"); Set<Map.Entry<Student, String>> entries = treeMap.entrySet();
for(Map.Entry<Student, String> entry : entries) {
System.out.println(entry.getKey() + "==========" + entry.getValue());
}
}
}

  结果:

Student{name='zs', age='10'}==========beijing
Student{name='ww', age='11'}==========hangzhou
Student{name='ls', age='12'}==========shenzhen
Student{name='zl', age='15'}==========guangzhou

  3、TreeMap(Map<? extends K, ? extends V> m)型

/**
* 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,根据key的自然排序将参数中m的元素存储到treeMap中。

  4、TreeMap(SortedMap<K, ? extends V> m)型

/**
* 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) {
}
}

  说明:定义一个新的treeMap,根据sortedMap中比较器的排序规则将参数中m的元素存储到treeMap中。

四、treeMap源码分析-核心函数

  4.1 增:put函数----存储元素

/**
* 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) {
//将根节点赋值给t
Entry<K,V> t = root;
if (t == null) {
//根节点为null,添加的是第一个元素,先check key的type
compare(key, key); // type (and possibly null) check //将第一个元素赋值给根节点
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;//定义比较结果
Entry<K,V> parent;//定义put进来的元素的父节点
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;//获取要存储元素的parent节点
//从root开始,比较两者key的大小
cmp = cpr.compare(key, t.key);
if (cmp < 0)
//小于已存在节点的key,将t.left赋值给t,以便再次进行比较
t = t.left;
else if (cmp > 0)
//大于已存在节点的key,将t.right赋值给t,以便再次进行比较
t = t.right;
else
//为0,表示两者相等,用value替换原value值,并返回原value
return t.setValue(value);
} while (t != null);//t != null,接着进行比较
}
else {
//The comparator used to maintain order in this tree map, or null if it uses the natural ordering of its keys.
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")//获取key的compareTo方法
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;//获取要存储元素的parent节点
//从root开始,比较两者key的大小
cmp = k.compareTo(t.key);
if (cmp < 0)
//小于已存在节点的key,将t.left赋值给t,以便再次进行比较
t = t.left;
else if (cmp > 0)
//大于已存在节点的key,将t.right赋值给t,以便再次进行比较
t = t.right;
else
//为0,表示两者相等,用value替换原value值,并返回原value
return t.setValue(value);
} while (t != null);//t != null,接着进行比较
}
//构建要存储的新元素
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)//小于0,位于parent的左边
parent.left = e;
else//大于0,位于parent元素的右边
parent.right = e;
//插入后进行修正
fixAfterInsertion(e);
size++;
modCount++;
return null;
}

  说明:插入一个元素时,若用户自定义一个比较器,则会按照用户自定义比较器中的排序规则确定元素的插入位置,否则,将会使用key自身实现的比较器确定插入位置。该方法主要是通过key的比较寻找插入节点的parent节点,并将parent节点与插入节点联系起来。

  4.2 删:remove和removeNode函数----删除元素

/**
* Removes the mapping for this key from this TreeMap if present.
*
* @param key key for which mapping should be removed
* @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 remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null; V oldValue = p.value;
deleteEntry(p);
return oldValue;
}

  通过getEntry获取要删除的节点

/**
* Returns this map's entry for the given key, or {@code null} if the map
* does not contain an entry for the key.
*
* @return this map's entry for the given key, or {@code null} if the map
* does not contain an entry for the 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
*/
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
//根据自定义的比较器的排序规则获取节点
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
//比较器为null,通过key的循环比较获取节点
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;
}

  再通过deleteEntry将该节点进行删除,这个方法没看懂,先放着 o(╥﹏╥)o

/**
* Delete node p, and then rebalance the tree.
*/
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; // Fix replacement
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;
}
}
}

  4.3 改:put函数----修改元素

  参见4.1中的put函数,可以将相同key的value覆盖。

  4.4 查:get和getEntry方法----查找元素

/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code key} compares
* equal to {@code k} according to the map's ordering, then this
* method returns {@code v}; otherwise it returns {@code null}.
* (There can be at most one such mapping.)
*
* <p>A return value of {@code null} does not <em>necessarily</em>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
*
* @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 get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}

  getEntry方法参见4.2中的getEntry。

五、总结

  由TreeMap我们可以知道其底层的数据结构为红黑树,并且可以使用用户自定义的比较器来实现比较逻辑。红黑树还是难了点,插入之后的旋转看着还是头晕,以后有时间在好好研究。

参考资料:

https://www.cnblogs.com/leesf456/p/5255370.html

https://www.cnblogs.com/xrq730/p/6867924.html

集合之TreeMap(含JDK1.8源码分析)的更多相关文章

  1. 集合之TreeSet(含JDK1.8源码分析)

    一.前言 前面分析了Set接口下的hashSet和linkedHashSet,下面接着来看treeSet,treeSet的底层实现是基于treeMap的. 四个关注点在treeSet上的答案 二.tr ...

  2. 集合之HashSet(含JDK1.8源码分析)

    一.前言 我们已经分析了List接口下的ArrayList和LinkedList,以及Map接口下的HashMap.LinkedHashMap.TreeMap,接下来看的是Set接口下HashSet和 ...

  3. 集合之LinkedHashSet(含JDK1.8源码分析)

    一.前言 上篇已经分析了Set接口下HashSet,我们发现其操作都是基于hashMap的,接下来看LinkedHashSet,其底层实现都是基于linkedHashMap的. 二.linkedHas ...

  4. 集合之HashMap(含JDK1.8源码分析)

    一.前言 之前的List,讲了ArrayList.LinkedList,反映的是两种思想: (1)ArrayList以数组形式实现,顺序插入.查找快,插入.删除较慢 (2)LinkedList以链表形 ...

  5. 集合之LinkedList(含JDK1.8源码分析)

    一.前言 LinkedList是基于链表实现的,所以先讲解一下什么是链表.链表原先是C/C++的概念,是一种线性的存储结构,意思是将要存储的数据存在一个存储单元里面,这个存储单元里面除了存放有待存储的 ...

  6. 集合之ArrayList(含JDK1.8源码分析)

    一.ArrayList的数据结构 ArrayList底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据.我们对ArrayList类的实例的所有的操作(增删改查等),其底层都 ...

  7. 集合之LinkedHashMap(含JDK1.8源码分析)

    一.前言 大多数的情况下,只要不涉及线程安全问题,map都可以使用hashMap,不过hashMap有一个问题,hashMap的迭代顺序不是hashMap的存储顺序,即hashMap中的元素是无序的. ...

  8. 【集合框架】JDK1.8源码分析之HashMap(一) 转载

    [集合框架]JDK1.8源码分析之HashMap(一)   一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化 ...

  9. 【集合框架】JDK1.8源码分析之ArrayList详解(一)

    [集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...

随机推荐

  1. [CQOI2016]手机号码

    嘟嘟嘟 这题一看就是数位dp. 我写数位dp,一般是按数位dp的格式写一个爆搜,然后加一点记忆化. 不过其实我一直不是很清楚记忆化是怎么加,感觉就是把dfs里的参数都扔到dp数组里,好像很暴力啊. 这 ...

  2. 010_动态语言与鸭子类型及python2和3的区别

    一. 动态语言中经常提到鸭子类型,所谓鸭子类型就是:如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子(If it walks like a duck and quacks like a duck, ...

  3. 手动设定统计数据 set_table_stats

    PROCEDURE SET_TABLE_STATS Argument Name Type In/Out Default? ------------------------------ -------- ...

  4. Pull is not possible because you have unmerged files

    Pull is not possible because you have unmerged files.   在git pull的过程中,如果有冲突,那么除了冲突的文件之外,其它的文件都会做为sta ...

  5. 比起Windows,怎样解读Linux的文件系统与目录结构?

    比起Windows,怎样解读Linux的文件系统与目录结构? Linux 和Windows的文件系统有些不同,在学习使用 Linux 之前,若能够了解这些不同,会有助于后续学习. 本文先对Window ...

  6. 吉特日化MES-日化行业原料仓库所见问题汇总

    2018年工作主要面向的是日化行业,其中包括日化生产以及日化生产原料仓库,和以往接触到仓库有点不一样在于日化行业原料的特性问题,日化行业的原料基本以粉尘和液体为主. 1. 原料的形态上: 日化行业原料 ...

  7. log4j打印堆栈信息

    原文地址:https://blog.csdn.net/xianyu_0418/article/details/6043174 大家都知道,网站在运行的过程中,打印必要的log对记录网站的运行情况.从而 ...

  8. Python全栈开发之路 【第十九篇】:Bootstrap

    一.下载和基本使用 官方地址:www.bootcss.com 二.响应式介绍 1.@meida 媒体查询 (1)响应式页面 为了页面能够适应不同工具的屏幕大小的限制,而开发的一种自适应页面,即 一次开 ...

  9. (第十三周)Final阶段用户调查报告

    项目名:食物链教学工具 组名:奋斗吧兄弟 组长:黄兴 组员:李俞寰.杜桥.栾骄阳.王东涵 用户调查报告 调查时间:2016年12月1日  21:00——2016年12月3日  12:00 项目分享链接 ...

  10. Mysql数据库触发器调用脚本

    一.数据库触发器 mysql触发器trigger 实例详解 对数据库触发器new和old的理解 示例 二.UDF mySql的UDF是什么 三.安装执行命令UDF mysql触发器调用外部脚本(安装) ...