一、TreeSet 概述
1、TreeSet 是 SortedSet 接口的实现类, TreeSet 可以确保集合元素处于排序状态。
2、TreeSet顾名思义他内部维护的是一个TreeMap,底层是红黑二叉树,他使得集合内都是有序的序列。
3、Tree 可以按照添加对象的指定属性,进行排序,所以向TreeSet中添加的数据,要求是相同类的对象。
4、两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator);
5、自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals();
6、定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals();
7、对 TreeSet 进行遍历的时候,默认是使用自然排序的规则来排序的;
8、TreeSet 添加自定义类的对象时候,必须要有自然排序或定制排序,否则抛出异常:cannot cast to java.lang.Comparable;
二、TreeSet 结构
1、TreeSet 声明
1 public class TreeSet<E> extends AbstractSet<E>
2 implements NavigableSet<E>, Cloneable, java.io.Serializable
2、TreeSet 类继承结构
3、红黑树
三、TreeSet 创建
1、构造器
TreeSet 是基于红黑树结构实现的,会对元素进行排序,TreeSet 提供了五种构造器:
1 public TreeSet() {
2 this(new TreeMap<E,Object>());
3 }
4
5 TreeSet(NavigableMap<E,Object> m) {
6 this.m = m;
7 }
8 public TreeSet(Comparator<? super E> comparator) {
9 this(new TreeMap<>(comparator));
10 }
11 public TreeSet(Collection<? extends E> c) {
12 this();
13 addAll(c);
14 }
15 public TreeSet(SortedSet<E> s) {
16 this(s.comparator());
17 addAll(s);
18 }
来看一下里面的 m 属性是什么:
1 /**
2 * The backing map.
3 */
4 private transient NavigableMap<E,Object> m;
5
6 // Dummy value to associate with an Object in the backing Map
7 private static final Object PRESENT = new Object();
可以看到里面是维护了一个 NavigableMap。
2、通过 Comparator 实例创建 TreeSet
上面的四个构造函数中着重要介绍第三个,它通过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
3、
四、TreeSet 方法
五、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 }
六、TreeSet 与 比较器
1、自然排序
2、定制排序
更多Java比较器知识:https://www.cnblogs.com/niujifei/p/14533868.html
七、案例
1、自定义类的自然排序
案例:
1 public class User implements Comparable{
2 private String name;
3 private int age;
4
5 public User() {
6 }
7
8 public User(String name, int age) {
9 this.name = name;
10 this.age = age;
11 }
12
13 public String getName() {
14 return name;
15 }
16
17 public void setName(String name) {
18 this.name = name;
19 }
20
21 public int getAge() {
22 return age;
23 }
24
25 public void setAge(int age) {
26 this.age = age;
27 }
28
29 @Override
30 public String toString() {
31 return "User{" +
32 "name='" + name + '\'' +
33 ", age=" + age +
34 '}';
35 }
36
37 @Override
38 public boolean equals(Object o) {
39 System.out.println("User equals()....");
40 if (this == o) return true;
41 if (o == null || getClass() != o.getClass()) return false;
42
43 User user = (User) o;
44
45 if (age != user.age) return false;
46 return name != null ? name.equals(user.name) : user.name == null;
47 }
48
49 @Override
50 public int hashCode() { //return name.hashCode() + age;
51 int result = name != null ? name.hashCode() : 0;
52 result = 31 * result + age;
53 return result;
54 }
55
56 //按照姓名从大到小排列,年龄从小到大排列
57 @Override
58 public int compareTo(Object o) {
59 if(o instanceof User){
60 User user = (User)o;
61 // return -this.name.compareTo(user.name);
62 int compare = -this.name.compareTo(user.name);
63 if(compare != 0){
64 return compare;
65 }else{
66 return Integer.compare(this.age,user.age);
67 }
68 }else{
69 throw new RuntimeException("输入的类型不匹配");
70 }
71
72 }
73 }
使用TreeSet保存:
1 @Test
2 public void test1(){
3 TreeSet set = new TreeSet();
4 set.add(new User("Tom",12));
5 set.add(new User("Jerry",32));
6 set.add(new User("Jim",2));
7 set.add(new User("Mike",65));
8 set.add(new User("Jack",33));
9 set.add(new User("Jack",56));
10
11
12 Iterator iterator = set.iterator();
13 while(iterator.hasNext()){
14 System.out.println(iterator.next());
15 }
16
17 }
注意:如果在自然比较器中并没有对年龄进行比较,当姓名一样时,就会认为这两个对象一样,不会再次存放;
如果在比较器中对两个属性都进行了比较,当有一个属性不一样就会放入 TreeSet中。
可以发现,这里对两个对象的比较并不是使用 equals() 方法,而是使用比较器来进行比较的。
2、自定义类的定制排序
定制排序:
1 @Test
2 public void test2(){
3 Comparator com = new Comparator() {
4 //按照年龄从小到大排列
5 @Override
6 public int compare(Object o1, Object o2) {
7 if(o1 instanceof User && o2 instanceof User){
8 User u1 = (User)o1;
9 User u2 = (User)o2;
10 return Integer.compare(u1.getAge(),u2.getAge());
11 }else{
12 throw new RuntimeException("输入的数据类型不匹配");
13 }
14 }
15 };
16
17 TreeSet set = new TreeSet(com); //按照给定的比较器进行排序
18 set.add(new User("Tom",12));
19 set.add(new User("Jerry",32));
20 set.add(new User("Jim",2));
21 set.add(new User("Mike",65));
22 set.add(new User("Mary",33));
23 set.add(new User("Jack",33));
24 set.add(new User("Jack",56));
25
26
27 Iterator iterator = set.iterator();
28 while(iterator.hasNext()){
29 System.out.println(iterator.next());
30 }
31 }
五、