TreeSet的整体架构
TreeSet 大致的结构和 HashSet 相似,底层组合的是 TreeMap,所以拥有 TreeMap key 能够排序的功能,迭代时,也可以按照 key 的排序顺序进行迭代。
public class TreeSet<E> extends AbstractSet<E>implements NavigableSet<E>, Cloneable, java.io.Serializable {// 底层的 mapprivate transient NavigableMap<E,Object> m;// 虚拟值,以关联一个对象在 map 的 value 值// 简而言之,该值是 TreeMap 中的 valueprivate static final Object PRESENT = new Object();/*** Constructs a set backed by the specified navigable map.*/TreeSet(NavigableMap<E,Object> m) {this.m = m;}/*** Constructs a new, empty tree set, sorted according to the* natural ordering of its elements. All elements inserted into* the set must implement the {@link Comparable} interface.* Furthermore, all such elements must be <i>mutually* comparable</i>: {@code e1.compareTo(e2)} must not throw a* {@code ClassCastException} for any elements {@code e1} and* {@code e2} in the set. If the user attempts to add an element* to the set that violates this constraint (for example, the user* attempts to add a string element to a set whose elements are* integers), the {@code add} call will throw a* {@code ClassCastException}.*/public TreeSet() {this(new TreeMap<E,Object>());}/*** Constructs a new, empty tree set, sorted according to the specified* comparator. All elements inserted into the set must be <i>mutually* comparable</i> by the specified comparator: {@code comparator.compare(e1,* e2)} must not throw a {@code ClassCastException} for any elements* {@code e1} and {@code e2} in the set. If the user attempts to add* an element to the set that violates this constraint, the* {@code add} call will throw a {@code ClassCastException}.** @param comparator the comparator that will be used to order this set.* If {@code null}, the {@linkplain Comparable natural* ordering} of the elements will be used.*/public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}/*** Constructs a new tree set containing the elements in the specified* collection, sorted according to the <i>natural ordering</i> of its* elements. All elements inserted into the set must implement the* {@link Comparable} interface. Furthermore, all such elements must be* <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a* {@code ClassCastException} for any elements {@code e1} and* {@code e2} in the set.** @param c collection whose elements will comprise the new set* @throws ClassCastException if the elements in {@code c} are* not {@link Comparable}, or are not mutually comparable* @throws NullPointerException if the specified collection is null*/public TreeSet(Collection<? extends E> c) {this();addAll(c);}/*** Constructs a new tree set containing the same elements and* using the same ordering as the specified sorted set.** @param s sorted set whose elements will comprise the new set* @throws NullPointerException if the specified sorted set is null*/public TreeSet(SortedSet<E> s) {this(s.comparator());addAll(s);}
TreeSet的
TreeSet的
TreeSet的
TreeSet复用TreeMap的不同方式
TreeSet 组合 TreeMap 实现的两种思路:
- TreeSet 直接使用 TreeMap 的某些功能,自己包装成新的 api
- TreeSet 定义自己想要的 api,自己定义接口规范,让 TreeMap 去实现
思路 1 和 2 的调用关系,都是 TreeSet 调用 TreeMap,但功能的实现关系完全相反,
第一种是:功能的定义和实现都在 TreeMap,TreeSet 只是简单的调用而已。
第二种是: TreeSet 把接口定义出来后,让 TreeMap 去实现内部逻辑,这样子的话因为接口是 TreeSet 定义的,所以实现一定是 TreeSet 最想要的,TreeSet 甚至都不用包装,可以直接把返回值吐出去都行。
场景一: TreeSet 的 add() ,其底层源码实现如下:
public boolean add(E e) {return m.put(e, PRESENT)==null;}
可以看到,底层直接使用的是 HashMap 的 put 的能力,直接拿来用。
场景二:TreeSet 的迭代 ,其底层源码实现如下:
// NavigableSet 接口,其定义了迭代的一些规范,和取值的一些特殊方法// TreeSet 实现了该方法,也就是说 TreeSet 本身已经定义了迭代的规范// 这是一个单独的接口(只写出了其中的两个方法,还有其他的)public interface NavigableSet<E> extends SortedSet<E> {// 按升序返回该集合中元素的迭代器Iterator<E> iterator();E lower(E e);}// m.navigableKeySet() 是 TreeMap 写了一个子类实现了 NavigableSet// 接口,实现了 TreeSet 定义的迭代规范// 这是 TreeSet 类中的方法public Iterator<E> iterator() {return m.navigableKeySet().iterator();}
从下面 TreeMap 的源码中可以看出 TreeMap 实现了 TreeSet 定义的各种特殊方法。
这种思路是 TreeSet 定义了接口的规范,TreeMap 负责去实现,实现思路和场景一是相反的。
// TreeMap 类中的部分源码// TreeMap 中对 NavigableSet 接口的实现源码static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {private final NavigableMap<E, ?> m;KeySet(NavigableMap<E,?> map) { m = map; }public Iterator<E> iterator() {if (m instanceof TreeMap)return ((TreeMap<E,?>)m).keyIterator();elsereturn ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();}public Iterator<E> descendingIterator() {if (m instanceof TreeMap)return ((TreeMap<E,?>)m).descendingKeyIterator();elsereturn ((TreeMap.NavigableSubMap<E,?>)m).descendingKeyIterator();}public int size() { return m.size(); }public boolean isEmpty() { return m.isEmpty(); }public boolean contains(Object o) { return m.containsKey(o); }public void clear() { m.clear(); }public E lower(E e) { return m.lowerKey(e); }public E floor(E e) { return m.floorKey(e); }public E ceiling(E e) { return m.ceilingKey(e); }public E higher(E e) { return m.higherKey(e); }public E first() { return m.firstKey(); }public E last() { return m.lastKey(); }public Comparator<? super E> comparator() { return m.comparator(); }public E pollFirst() {Map.Entry<E,?> e = m.pollFirstEntry();return (e == null) ? null : e.getKey();}public E pollLast() {Map.Entry<E,?> e = m.pollLastEntry();return (e == null) ? null : e.getKey();}public boolean remove(Object o) {int oldSize = size();m.remove(o);return size() != oldSize;}public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,E toElement, boolean toInclusive) {return new KeySet<>(m.subMap(fromElement, fromInclusive,toElement, toInclusive));}public NavigableSet<E> headSet(E toElement, boolean inclusive) {return new KeySet<>(m.headMap(toElement, inclusive));}public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {return new KeySet<>(m.tailMap(fromElement, inclusive));}public SortedSet<E> subSet(E fromElement, E toElement) {return subSet(fromElement, true, toElement, false);}public SortedSet<E> headSet(E toElement) {return headSet(toElement, false);}public SortedSet<E> tailSet(E fromElement) {return tailSet(fromElement, true);}public NavigableSet<E> descendingSet() {return new KeySet<>(m.descendingMap());}public Spliterator<E> spliterator() {return keySpliteratorFor(m);}}
TreeSet的常见问题
说一下TreeSet的使用场景
答:我们一般都是在需要把元素进行排序的时候使用 TreeSet。
使用 TreeSet 时需要注意,元素最好实现 Comparable 接口,这样方便底层的 TreeMap 根据 key 进行排序。
