TreeSet的整体架构
TreeSet 大致的结构和 HashSet 相似,底层组合的是 TreeMap,所以拥有 TreeMap key 能够排序的功能,迭代时,也可以按照 key 的排序顺序进行迭代。
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
// 底层的 map
private transient NavigableMap<E,Object> m;
// 虚拟值,以关联一个对象在 map 的 value 值
// 简而言之,该值是 TreeMap 中的 value
private 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();
else
return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
}
public Iterator<E> descendingIterator() {
if (m instanceof TreeMap)
return ((TreeMap<E,?>)m).descendingKeyIterator();
else
return ((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 进行排序。