源码分析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和TreeSet

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进行对比。


先看看这两个的关系图:

源码分析TreeMap和TreeSet

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的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点# Map - TreeSet & TreeMap 源码解析

死磕 java集合之TreeSet源码分析

TreeSet 源码分析

TreeSet和TreeMap部分源码解析

TreeSet源码分析

TreeSet源码分析