源码分析TreeMap和TreeSet
Posted IT技术栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析TreeMap和TreeSet相关的知识,希望对你有一定的参考价值。
对于HashMap和HashSet大家可能用的多一点,本文将通过源码对TreeMap和TreeSet的底层原理和数据结构进行分析,并拿TreeMap和HashMap进行比较,TreeSet和HashSet进行比较,分析他们各自的使用场景。
TreeMap
TreeMap是Map的一种,说到Map可能我们用的最多的就是HashMap,那TreeMap和HashMap又有什么不同的地方呢?
我们先对比一下TreeMap和HashMap的类关系图。
HashMap
TreeMap
从上面2张图可以看出,TreeMap比HashMap多实现了一个NavigableMap接口,NavigableMap是实现了SortedMap的接口。
SortedMap接口定义了一个返回Comparator的方法,这是用来比较大小和排序用的。
Comparator comparator();
NavigableMap定义了一些遍历访问Map数据的方法。部分方法罗列如下:
//用于返回比指定key小的最大key对应的键值对
//TreeMap的数据是按key进行比较和排序的,返回比传入key小的下一个key对应的键值对
Map.EntrylowerEntry(K key);
//返回比指定key小或等于与该key的键值对
Map.EntryfloorEntry(K key);
//返回比指定key大或等于该key的减值对
Map.EntryceilingEntry(K key);
//与lowerEntry方法相反,返回比指定key大的最小键值对
Map.EntryhigherEntry(K key);
到此,我们可以知道TreeMap是一个有序的Map,且具备查找某个数据的上一个、下一个数据等功能的Map。
那TreeMap类内部自身又是怎么实现的?
我们通过源码来解析:
要想了解集合类的源码,作者的经验是要先看添加方法,这里我们就先看看它的put方法。
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
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;
// 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);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
可以看出,数据是存储在Entry类型的数据结构中,且这个Entry的实例有left、right属性。
通过查看Entry的源码,发现它还有下面几个属性:
Entry left;
Entry right;
Entry parent;
boolean color = BLACK;
通过上面几个属性,再加上对该数据结构的插入操作可以看出TreeMap的底层是红黑树的数据结构。
那我们再总结一下,TreeMap是一个有序的Map,底层数据结构是红黑树,提供了各种遍历访问的方法(包括取上一个、下一个数据的方法)。
那TreeMap相较HashMap又有什么不一样呢?什么时候选HashMap,又什么时候选TreeMap?
关于HashMap的和以及代码分析在之前的文章已经有介绍过,这里就不再细说。
首先,TreeMap比HashMap多了一个有序的功能,所以如果我们需要根据key进行排序的时候,可以选择TreeMap。
另一方面,HashMap从jdk1.8开始存在两种形态的数据结构,即数组+链表,数组+红黑树。而TreeMap只是红黑树的数据结构。当数据量很大的时候TreeMap树的层数会比HashMap多,因为HashMap是树的数组,有多个树结构。作者测试了10万条的数据遍历10万下逐个调用get方法查询,发现HashMap会比TreeMap快一点,但是相差也不大,大概二三十毫秒。
TreeSet
TreeSet是Set的一种,同样,这里我们也拿TreeSet和HashSet进行对比。
先看看这两个类的关系图:
HashSet
TreeSet
可以看出TreeSet比HashSet多实现了一个NavigableSet接口,NavigableSet是继承了SortedSet的接口。这里和上面的TreeMap与HashMap的比对结果差不多。
正如大家所想,TreeSet相较HashSet也是多出排序功能,然后提供对应的遍历访问接口。
那TreeSet的数据结构又是怎样的?
我们通过源码来看:
private transient NavigableMapm;
TreeSet(NavigableMap<< span="">E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<< span="">E,Object>());
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
这里发现很神奇的地方,TreeSet居然是通过TreeMap来存储数据的。
没错,虽然TreeSet是单个数据的集合,而TreeMap是键值对类型的数据。TreeSet只需要对value值填一个无意义的对象即可,遍历时只返回key值。
实际HashSet是也是通过HashMap来存储数据的。
精选推荐:
*本文为IT技术栈原创文章,独家版权归于本平台,受到原创保护。任何渠道的转载请后台留言联系授权,侵权必究。
以上是关于源码分析TreeMap和TreeSet的主要内容,如果未能解决你的问题,请参考以下文章