一、介绍
在上期文章中,我们从源码层面详细分析了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 applicable if (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的方法对红黑树进行操作。
纸上得来终觉浅,绝知此事要躬行。
————————————————我是万万岁,我们下期再见————————————————