往期文章
- 用最简单的话讲最明白的红黑树
- java源码阅读 - HashMap
- 数据结构 - 堆与堆排序
文章目录
- 往期文章
- 一、介绍
- 二、类的声明
- 三、成员变量
- 四、构造函数
- 五、常用方法
- 1. NavigableSet接口的实现
- 2. SortedSet接口的实现
- 六、总结
一、介绍
在上期文章中,我们从源码层面详细分析了java集合框架中Set一族的实现HashSet,它的内部维护了一个HashMap对象作为内部存储,也就是说HashSet的底层结构为哈希表,今天我们介绍Set的另一个实现——TreeSet,对标HashSet与HashMap的关系,我们猜想TreeSet可能会维护一个TreeMap作为内部存储,事实也确实如此,因此,TreeSet的特性均继承于TreeMap,如元素有序等。在学习TreeSet源码之前,必须对TreeMap的源码了如指掌。由于TreeMap底层实现为较复杂的红黑树,对TreeMap源码不了解的同学请一定要参考前面的文章TreeMap源码。下面我们先看一下TreeSet的UML图。
二、类的声明
public class TreeSet<E> extends AbstractSet<E>implements NavigableSet<E>, Cloneable, java.io.Serializable
从类的声明和上面的UML类图中可以了解到:
- 继承AbstractSet抽象类,对其进行了扩展。
- 实现了NavigableSet接口,表示它是一个提供导航功能的Set集合,满足Set集合的定义
- 实现了Cloneable接口,提供了对象克隆方法,但请注意,是浅克隆。
- 实现了Serializable接口,支持序列化。
三、成员变量
// HashSet的底层使用NavigableMap对象作为存储结构,
// 目前我们常用的NavigableMap实现为TreeMap
// 而TreeMap已经实现了红黑树,因此TreeSet无需再次对红黑树进行实现,直接通过TreeMap对红黑树进行数据的存取即可。
private transient NavigableMap<E,Object> m;// 虽然TreeSet使用TreeMap来操作红黑树,
// 但是TreeMap存储的是<key,value>键值对,
// 因此TreeSet在保存数据时,key为实际保存的数据,使用一个固定的对象PRESENT作为假的value值
private static final Object PRESENT = new Object();
四、构造函数
TreeSet提供了四个构造函数,由于TreeSet的底层为TreeMap,因此在TreeSet的构造方法中,都是先实例化一个TreeMap对象。
在TreeSet的众多构造函数中,有一个比较特殊的构造函数
TreeSet(NavigableMap<E,Object> m) {this.m = m;
}
该构造函数的访问修饰符为默认,因此只能被类内部和同包内的子类调用。通过接收一个NavigableMap
对象,并将其作为内部的NavigableMap
对象维护,比如TreeMap。
-
无参构造
通过TreeMap的无参构造,实例化一个TreeMap对象,作为TreeSet的内部成员变量。
public TreeSet() {this(new TreeMap<E,Object>()); }
-
指定比较器
Comparator
其实还是调用TreeMap的构造方法
public TreeMap(Comparator<? super K> comparator)
来实例化一个TreeMap对象,作为TreeSet的内部成员变量。public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator)); }
-
通过传入一个集合构造
先通过无参构造,实例化TreeSet对象,然后将集合中的元素通过
addAll()
方法批量保存到TreeSet对象中。其中
addAll()
方法的实现与TreeMap中putAll()
方法的实现几乎完全相同,故不再赘述,可查看前面的文章TreeMappublic TreeSet(Collection<? extends E> c) {this();addAll(c); }public boolean addAll(Collection<? extends E> c) {// Use linear-time version if applicableif (m.size()==0 && c.size() > 0 &&c instanceof SortedSet &&m instanceof TreeMap) {SortedSet<? extends E> set = (SortedSet<? extends E>) c;TreeMap<E,Object> map = (TreeMap<E, Object>) m;Comparator<?> cc = set.comparator();Comparator<? super E> mc = map.comparator();if (cc==mc || (cc != null && cc.equals(mc))) {map.addAllForTreeSet(set, PRESENT);return true;}}return super.addAll(c); }
-
通过传入一个有序集合构造
在有序集合
SortedSet
中,提供给我们一个方法comparator()
来获取有序集合中的比较器,再根据这个比较器来实例化一个TreeSet对象。如果有序集合中不存在比较器Comparator
,则从TreeMap中我们也知道,键值对中的key必须实现Compareable
接口中的compareTo()
方法。其中
addAll()
方法的实现与TreeMap中putAll()
方法的实现几乎完全相同,故不再赘述,可查看前面的文章TreeMap。public TreeSet(SortedSet<E> s) {this(s.comparator());addAll(s); }
五、常用方法
由于TreeSet实现NavigableSet
接口,NavigableSet
接口又继承于SortedSet
接口。而TreeMap实现NavigableMap
接口,NavigableMap
接口又继承于SortedMap
接口,如下图所示:
二者相似度极高,因此我们可以断定
-
SortedSet
接口的方法都是通过调用TreeMap
中实现SortedMap
接口方法而实现的。 -
NavigableSet
接口的方法都是通过调用TreeMap
中实现NavigableMap
接口方法而实现的。
下面我们通过源码进行分析:
1. NavigableSet接口的实现
-
lower()
获取比指定元素小的元素中最大的一个元素。调用内部TreeMap对象实现
NavigableMap
接口的lowerKey()
方法,public E lower(E e) {return m.lowerKey(e); }
-
floor()
获取小于等于指定元素的元素中最大的一个元素。调用内部TreeMap对象实现
NavigableMap
接口的floorKey()
方法,public E floor(E e) {return m.floorKey(e); }
-
ceiling()
获取大于等于指定元素的元素中最小的一个元素。调用内部TreeMap对象实现
NavigableMap
接口的ceilingKey()
方法,public E ceiling(E e) {return m.ceilingKey(e); }
-
higher()
获取比指定元素大的元素中最小的一个元素。调用内部TreeMap对象实现
NavigableMap
接口的higherKey()
方法,public E higher(E e) {return m.higherKey(e); }
-
pollFirst()
获取set集合中第一个元素,并将其删除。调用内部TreeMap对象实现
NavigableMap
接口的pollFirstEntry()
方法,获取首个键值对节点并删除后,返回该键值对节点中的key。public E pollFirst() {Map.Entry<E,?> e = m.pollFirstEntry();return (e == null) ? null : e.getKey(); }
-
pollLast()
获取set集合中最后一个元素,并将其删除。调用内部TreeMap对象实现
NavigableMap
接口的pollLastEntry()
方法,获取最后一个键值对节点并删除后,返回该键值对节点中的key。public E pollLast() {Map.Entry<E,?> e = m.pollLastEntry();return (e == null) ? null : e.getKey(); }
2. SortedSet接口的实现
-
comparator()
获取当前实例中的比较器
public Comparator<? super E> comparator() {return m.comparator(); }
-
first()
获取set集合中第一个元素。调用内部TreeMap对象实现
SortedMap
接口的firstKey()
方法,获取首个键值对节点中的key。public E first() {return m.firstKey(); }
-
last()
获取set集合中最后一个元素。调用内部TreeMap对象实现
SortedMap
接口的lastKey()
方法,获取最后一个键值对节点中的key。public E last() {return m.lastKey(); }
六、总结
- 元素有序,基于比较器或
Compareable
接口的compareTo()
方法实现元素的排序 - 线程不安全
- TreeSet的底层实现为TreeMap,TreeMap的底层实现为红黑树,因此TreeSet的底层实现为红黑树,TreeSet通过调用TreeMap的方法对红黑树进行操作。
纸上得来终觉浅,绝知此事要躬行。
————————————————我是万万岁,我们下期再见————————————————