TreeSet

前言:

Set接口有三个实现类,HashSet,LinkedHashSet和TreeSet。HashSet前文已经介绍过了,他内部通过维护一个HashMap存储数据,LinkedHashSet继承了HashSet,他与HashSet的唯一区别就是他维护的是一个LinkedHashMap,实现了双向链表的功能,维护数据插入的顺序,这部分内容我将在介绍Map类的时候详细介绍。TreeSet也就是本文要介绍的这个实现类,顾名思义他内部维护的是一个TreeMap,底层是红黑二叉树,他使得集合内都是有序的序列。写Set这部分的时候让我很纠结,set有点像一个精简版的map,他核心的功能都是用了map的实现,但是功能又远没有map强大。关于这点让我很困惑,或许作者设计这个接口的用意就是希望用户能快速简单的操作集合元素,而不需要像map那么复杂。
TreeSet实现的部分和HashSet非常类似,我不太希望重复描述哪些相似的地方,而把精力放在重点部分,读者可以结合HashSet一起阅读。

一、Comparator和TreeSet的创建

TreeSet的创建和HashSet类似,创建的是一个TreeMap实例。TreeMap是一个什么存在呢?简单的说,他是基于红黑树结构实现的,会对元素进行排序。下面来看一看他的构造过程。

  1. public TreeSet() {
  2. this(new TreeMap<E,Object>());
  3. }
  4. public TreeSet(Comparator<? super E> comparator) {
  5. this(new TreeMap<>(comparator));
  6. }
  7. public TreeSet(Collection<? extends E> c) {
  8. this();
  9. addAll(c);
  10. }
  11. public TreeSet(SortedSet<E> s) {
  12. this(s.comparator());
  13. addAll(s);
  14. }

上面的四个构造函数中我着重要介绍第二个,他通过Comparator实例来创建TreeMap,那么Comparator到底是何方神圣呢?通过阅读Comparator的源码发现,这是一个用于集合类排序的辅助接口,用户需要实现compare方法。如果用户用了这种方式创建TreeSet,那么集合元素就不需要做额外处理,否则集合元素都需要实现Comparable接口,因为Tree在排序的时候会调用compare或者compareTo方法(介绍TreeMap的时候会具体讲解)。下面来看看我写的一个样例代码。

  1. public class MyComparator implements Comparator<Person> {
  2. @Override
  3. public int compare(Person o1, Person o2) {
  4. return o1.age - o2.age;
  5. }
  6. }
  7. public class Person {
  8. public Integer age;
  9. public Person(Integer value) {
  10. this.age = value;
  11. }
  12. }
  13. public static void TreeSetTest() {
  14. // TreeMap在底层put元素的时候会判断是否存在Comparator实例,如果存在,则每次添加元素排序比较的时候会调用compare接口。
  15. TreeSet<Person> set = new TreeSet<Person>(new MyComparator());
  16. Person p1 = new Person(1);
  17. Person p2 = new Person(1);
  18. Person p3 = new Person(5);
  19. Person p4 = new Person(9);
  20. Person p5 = new Person(10);
  21. set.add(p1);
  22. set.add(p2);
  23. set.add(p3);
  24. set.add(p4);
  25. set.add(p5);
  26. Iterator<Person> i = set.iterator();
  27. while (i.hasNext()) {
  28. Person p = (Person) i.next();
  29. System.out.println(p.age);
  30. }
  31. }
  32. 打印结果:
  33. 1
  34. 5
  35. 9
  36. 10

二、NavigableSet接口介绍

  1. // 返回比当前元素小的最近的一个元素
  2. public E lower(E e) {
  3. return m.lowerKey(e);
  4. }
  5. // 返回小于等于当前元素的最近一个元素
  6. public E floor(E e) {
  7. return m.floorKey(e);
  8. }
  9. // 返回大于等于当前元素的最近一个元素
  10. public E ceiling(E e) {
  11. return m.ceilingKey(e);
  12. }
  13. // 返回大于当前元素的最近一个元素
  14. public E higher(E e) {
  15. return m.higherKey(e);
  16. }