说到TreeMap,我们不得不提HashMap;因为他俩确实很像。具体的HashMap的相关分析可以去参考https://blog.csdn.net/zhao_xinhu/article/details/82740652
我们先来看下TreeMap和HashMap的结构对比图(左边为HashMap,右边为TreeMap)。
从上面的结构图中我们可以清晰的看出来,两个结构确实很像,虽然很像,但是我们一眼还是能看出来有区别,TreeMap比HashMap多实现了NavigableMap接口,而NavigableMap又实现了SortedMap接口;那么这两个接口其实就是TreeMap和HashMap的区别了,TreeMap比HashMap多了NavigableMap和SortedMap的相关功能。我们去具体看下相关代码。
1、SortedMap这个名字,肯定知道这个接口是提供了排序功能,那么TreeMap肯定也是具有排序功能的,我们随后再看是如何排序的,我们先来大概看下SortedMap中都有什么具体方法:
总共也就10个左右方法,我们大概看一下,都是获取key和value的,但是具体实现也要去看TreeMap中具体是怎么实现的,这里就不具体分析了。
2、我们看下NavigableMap这个接口中的方法都有哪些:
这里方法多一些,我们单看方法名称,也是获取key、value的,但是有些可以获取比指定key小或者大的对象信息;
大概看了下TreeMap的结构,我们下来看下TreeMap的部分源码。
3.1、既然可以排序,那么我们看下他是如何进行排序的。直接看put方法
public V put(K key, V value) {
Entry t = root;
//1、 这里是初始化Map的
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
// 初始化Map END
//2、 下面if else区分了是否有指定的排序规则
int cmp;
Entry parent;
// split comparator and comparable paths
Comparator super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
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);
}
//if else区分了是否有指定的排序规则 END
// 3、 将指定的值放入到map链表中
Entry e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
我把TreeMap的put方法分成了三个部分;1、首次put,初始化;2、非首次put,比较新key和老key排序;3、根据步骤2比较的值来决定是放到链表的left还是right(也就是放到老对象的前面还是后面)
1、 初始化的时候这里不多说,但是需要提醒一点,compare(key, key)这行代码会过滤key = null的这种情况,大家可以自行看下
2、 我们拿没有指定Comparator来分析,默认排序规则
// key不能为null
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);
这里文字不太好描述,我们举个例子:
假如,TreeMap中已经存在一个对象了,key = "b"; value="b";我们执行put("a", "a");
执行到do while循环这里,那么 t = Entry("b", "b", null);这里的null为parent;我们(cmp = "a".compareTo("b")) < 0;也就会执行t=t.left;这时候 t就变成null了,因为left没有值do while会直接跳出了
3、 执行到第三步,我们new 了一个Entry("a", "a", parent); 这里parent为Entry("b", "b", null);将parnet.left指向新节点,也就是a插入到b的前面了
来,我们上图:
那如果这时候插入一个Entry("c", "c")节点呢?
这样的话,TreeMap的数据结构也比较清晰了,很明显就是一棵树嘛,跟它的名字也是匹配的。和HashMap的数组+链表还是有有区别的,TreeMap就是通过这种链表的方式构建了一颗排序树,通过每次put来compareTo来比较哪个大,哪个小进行left和reight关联的。