TreeSet是Set接口的另一个实现类,它内部采用自平衡的排序二叉树来存储元素,这样的结构可以保证TreeSet集合中没有重复的元素,并且可以对元素进行排序。
package com.first;
import java.util.TreeSet;
public class HelloWorld {
public static void main(String[] args) {
TreeSet<Integer> ts=new TreeSet<>();
ts.add(3);
ts.add(2);
ts.add(2);
ts.add(3);
ts.add(1);
System.out.println(ts);
}
}
运行结果为
[1, 2, 3]
TreeSet存储自定义对象
package com.first;
import java.util.TreeSet;
public class HelloWorld {
public static void main(String[] args) {
TreeSet<Person> ts=new TreeSet<>();
ts.add(new Person("张三",23));
ts.add(new Person("李四",13));
ts.add(new Person("王五",43));
ts.add(new Person("赵六",33));
System.out.println(ts);
}
}
class Person{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
运行结果为
Exception in thread "main" java.lang.ClassCastException: com.first.Person cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1188)
at java.util.TreeMap.put(TreeMap.java:531)
at java.util.TreeSet.add(TreeSet.java:255)
at com.first.HelloWorld.main(HelloWorld.java:8)
报了类型转换异常的错误,无法转换成Comparable。要进行排序,需要告诉程序根据什么规则进行排序。Person类需要实现Comparable这个接口,并重写里边的compareTo方法。
class Person implements Comparable<Person>{
//省略的代码同上
@Override
public int compareTo(Person p) {
return 0;
}
}
运行结果为
[Person [name=张三, age=23]]
add张三的时候,会将其作为树根。add李四的时候会调用compareTo方法,返回值为0,就会认为李四和张三是一样的,然后就不存了,后面的依次类推。
改为return 1;
,运行结果为
[Person [name=张三, age=23], Person [name=李四, age=13], Person [name=王五, age=43], Person [name=赵六, age=33]]
add张三的时候,会将其作为树根。add李四的时候会调用compareTo方法,返回值为正数,然后存到右边。add王五的时候也会调用compareTo方法,先和张三比,返回正数,放到李四的右边,再和李四比,也是返回正数,放到李四的右边。赵六同上。取的时候按照中序遍历的顺序取。
改为return -1;
,运行结果为
[Person [name=赵六, age=33], Person [name=王五, age=43], Person [name=李四, age=13], Person [name=张三, age=23]]
这次返回值为负数,将李四放到张三的左边。
在TreeSet集合如何存储元素取决于compareTo方法的返回值
- 返回0,先看有没有,没有就存上,有的话就不存了
- 返回负数,会存在左边(可以理解为新加的元素比原有元素小,放到其左边)
- 返回正数,会存在右边(可以理解为新加的元素比原有元素大,放到其右边)
- 取的时候通过中序遍历取(可以理解为从小到大取)
新需求:按照年龄来排序
将comparTo方法改为
@Override
public int compareTo(Person p) {
return this.age-p.age;
}
运行结果为
[Person [name=李四, age=13], Person [name=张三, age=23], Person [name=赵六, age=33], Person [name=王五, age=43]]
add张三,将其作为根元素。add李四,李四会调用compareTo方法,13-23=-10,是一个负数,放到张三的左边。add王五,放到右边。add赵六,先和张三比,33-23=10,为正数,放到右边,再和王五比,33-43=-10,为负数,放到王五的左边。取的时候按照中序遍历取。
第二种比较的方法,指定比较器。
有时候,定义的类没有实现Comparable接口或者实现了Comparable接口而不想按照定义的compareTo()方法进行排序。例如,希望字符串可以按照长度来进行排序,这时可以通过自定义比较器的方式对TreeSet集合中的元素排序。
package com.first;
import java.util.TreeSet;
public class HelloWorld {
public static void main(String[] args) {
TreeSet<String> ts=new TreeSet<String>();
ts.add("aaaaaa");
ts.add("z");
ts.add("wc");
ts.add("bdc");
ts.add("b");
System.out.println(ts);
}
}
运行结果为
[aaaaaa, b, bdc, wc, z]
String类默认已经实现了Comparable接口,重写compareTo方法按字典顺序比较两个字符串。
现在我们想按照字符串的长度来进行排序
package com.first;
import java.util.Comparator;
import java.util.TreeSet;
public class HelloWorld {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<String>(new CompareByLen());
ts.add("aaaaaa");
ts.add("z");
ts.add("wc");
ts.add("bdc");
ts.add("b");
System.out.println(ts);
}
}
class CompareByLen implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
int num = s1.length() - s2.length();
return num==0?s1.compareTo(s2):num;//长度相同的话再比较一下内容
}
}
运行结果为
[b, z, wc, bdc, aaaaaa]
总结
TreeSet是用来排序的
使用方法
- 自然顺序(Comparable)
- TreeSet的add方法会把存入的对象提升为Comparable类型
- 调用对象的compareTo方法和集合中的对象比较
- 根据compareTo方法返回的结果进行存储
- 比较器顺序(Comparator)
- 创建TreeSet的时候可以指定一个Comparator
- 如果传入了Comparator的子类对象,那么TreeSet就会按照比较器中的规则排序
- add方法内部会自动调用Comparator接口中的compare方法排序
- 两种方式的区别
- TreeSet构造函数什么都不传,默认就按照类中Comparable的顺序(没有就报ClassCastException)
- TreeSet如果传入了Comparator,就优先按照Comparator