1.整体架构
TreeSet的结构大致与HashSet相似,底层依旧使用组合的方式,组合基础类TreeMap。所以能够对key进行排序。迭代的时候能够按照key的排序顺序进行迭代。
2.源码解析
TreeSet复用TreeMap的两种思路,
复用思路1
这一思路与HashSet复用HashMap的思路相同,
private transient NavigableMap<E,Object> m;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
这里并没有直接使用TreeMap,而是创建了一个NavigableMap对象(具体原因之后会说)。利用的同样是TreeMap中key唯一且具有顺序的特性。
复用思路2
TreeSet类的定义如下,
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
该类在定义过程中实现了NavigableSet接口。
TreeSet元素迭代过程中,实现迭代的思路,
// NavigableSet 接口,定义了迭代的一些规范,和一些取值的特殊方法
// TreeSet 实现了该方法,也就是说 TreeSet 本身已经定义了迭代的规范
public interface NavigableSet<E> extends SortedSet<E> {
Iterator<E> iterator();
E lower(E e);
}
// m.navigableKeySet() 是 TreeMap 写了一个子类实现了 NavigableSet接口,实现了 TreeSet 定义的迭代规范
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
TreeMap中NavigableSet接口的实现如下,

TreeMap实现了TreeSet定义的各种NavigableSet接口的方法。这里复用TreeMap的思路是,TreeSet定义接口规范,但具体的实现靠的是TreeMap中相同的接口规范进行实现。
总结
复用思路总结
TreeSet组合TreeMap的两种思路总结如下,
- TreeSet直接使用TreeMap的某些功能,包装成自身的API,如add方法。
- TreeSet定义好接口规范,让TreeMap去实现。
这两种思路都是TreeSet调用TreeMap,但是功能的实现关系完全相反。第一种是功能的定义和实现都在TreeMap中,TreeSet只需要简单调用即可。第二种是TreeSet将接口定义出来,让TreeMap去实现内部逻辑(TreeSet负责接口定义,TreeMap用于实现)。
使用这两种思路复用的原因,
- 像add这种简单方法,直接使用思路1,主要是add的实现没有复杂的逻辑,TreeSet自己实现起来比较简单。
- 思路2主要用于复杂场景(如迭代场景)。比如需要能从头迭代、取到第一个值、取最后一个值等复杂操作,此外TreeMap底层结构复杂。这时最好让TreeSet实现接口定义,TreeMap去实现功能。TreeMap对自身底层的复杂结构非常清楚,实现起来准确又简洁。
什么场景下使用TreeSet
一般需要保证元素唯一的同时对元素进行排序时使用TreeSet,使用时最好元素先实现Comparable接口,这样便于底层的TreeMap依据key进行排序。
本文详细解析了TreeSet如何通过两种复用思路利用TreeMap的特性,实现元素排序与迭代功能。一种是直接调用TreeMap的简单方法,另一种是在TreeSet中定义接口规范,由TreeMap实现复杂逻辑。
1109

被折叠的 条评论
为什么被折叠?



