TreeSet底层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap,通过key来存储HashSet的元素。由于map中key都是不可重复的,因此,TreeSet天然具有“不可重复”的特性。
我们打开TreeSet的源码,发现里面有一行核心代码:
TreeMap是NavigableMap(基于红黑树)的实现类,而TreeSet底层使用NavigableMap来保存元素,所以说TreeSet内部维持了一个简化版的TreeMap,通过key来存储元素。
- TreeSet****的使用
TreeSet是用来排序的,可以指定一个顺序,对象存入之后会按照指定的顺序排列,TreeSet中的元素支持2种排序方式:
- 自然排序:通过实现Comparerable接口 ,并且重写compareTo方法。
TreeSet类的add()方法中会把存入的对象提升为Comparable类型, 调用对象的compareTo() 方 法和集合中的对象比较(当前存入的是谁,谁就会调用compareTo方法),根据compareTo() 方法返回的结果进行存储。
- 比较器排序:通过实现Comparetor接口,并且重写compare方法。
创建TreeSet的时候可以制定一个Comparator,如果传入了Comparator的子类对象,那么TreeSet 就会按照比较器中的顺序排序。add()方法内部会自动调用Comparator接口中compare()方法排 序。调用的对象(就是当前存入的对象)是compare方法的第一个参数,集合中的对象(已经 添加进去的对象)是compare方法的第二个参数。
- 自然排序
给TreeSet 中存储 JavaAPI 中提供的类型元素时,不需要实现Comparerable接口,因为在 JavaAPI 的每个类中已经重写完毕,如 String 类、Integer 类等,默认排序为升序。
【示例】HashSet存储 String
// 实例化一个TreeSet对象
TreeSet<String> set = new TreeSet<String>();
// 添加元素
set.add("bbb");
set.add("ccc");
set.add("aaa");
set.add("eee");
set.add("ddd");
set.add("eee"); // 只能输出一个“eee”
// 遍历元素
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
默认排序输出结果如下(两个“eee”只输出一个):
aaa
bbb
ccc
ddd
eee
给 HashSet 中存放自定义类型元素时,需要实现Comparerable接口并重写compareTo方法,否则抛出java.lang.ClassCastException异常。
【示例】创建自定义对象 Person
class Person implements Comparable<Person> {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 重写compareTo方法
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
因为TreeSet要对元素进行排序,那你排序的依据是什么,姓名还是年龄还是其它的,得告诉它,怎么告诉?需要让Person类实现Comparable接口并重写compareTo方法。
当compareTo方法返回值为负数,表示放在红黑树的左边,即逆序(降序)输出。
当compareTo方法返回值为零,表示元素相同,仅存放第一个元素(保证元素的唯一)。
当compareTo方法返回值为正数,表示放在红黑树的右边,即顺序(升序)输出。
【示例】创建 TreeSet 集合,存储 Person 对象
public class TreeSetDemo {
public static void main(String[] args) {
// 实例化一个TreeSet对象
TreeSet<Person> set = new TreeSet<Person>();
// 添加元素
set.add(new Person("张三", 23));
set.add(new Person("李四", 31));
set.add(new Person("王五", 24));
set.add(new Person("赵六", 31));
set.add(new Person("王麻子", 27));
// 遍历元素
Iterator<Person> iterator = set.iterator();
while(iterator.hasNext()) {
Person p = iterator.next();
System.out.println("name:" + p.name + " age:" + p.age);
}
}
}
自定义排序输出结果如下:
name:张三 age:23
name:王五 age:24
name:王麻子 age:27
name:李四 age:31
注意:因为compareTo方法按照年龄进行排序,而“李四”和“赵六”年龄相同,所以后添加的“赵六”就无法保存(唯一性)。
- 比较器排序
TreeSet构造方法什么都不传,默认按照类中Comparable的顺序排序(没有就抛出ClassCastException异常),当TreeSet的构造方法如果传入Comparator对象,就优先按照Comparator接口的compare方法进行排序。
当compare方法返回值为负数,表示放在红黑树的左边,即逆序(降序)输出。
当compare方法返回值为零,表示元素相同,仅存放第一个元素(保证元素的唯一)。
当compare方法返回值为正数,表示放在红黑树的右边,即顺序(升序)输出。
【示例】使用Comparator实现升序排序
// Student类
class Student {
private String name;
private int age;
public Student() {}
public Student(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;
}
}
// 测试类
public class TreeSetDemo {
public static void main(String[] args) {
// 实例化一个set对象,并按照年龄进行升序
Set<Student> set = new TreeSet<Student>(new Comparator<Student>() {
@Override // 按照年龄进行比较排序
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
// 添加元素
set.add(new Student("张三", 23));
set.add(new Student("李四", 31));
set.add(new Student("王五", 24));
set.add(new Student("赵六", 31));
set.add(new Student("王麻子", 27));
// 遍历set
Iterator<Student> iterator = set.iterator();
while (iterator.hasNext()) {
Student stu = iterator.next();
System.out.println("name:" + stu.getName() + " age:" + stu.getAge());
}
}
}
自定义排序输出结果如下:
name:张三 age:23
name:王五 age:24
name:王麻子 age:27
name:李四 age:31
注意:因为compare方法按照年龄进行排序,而“李四”和“赵六”年龄相同,所以后添加的“赵六”就无法保存(唯一性)。
- 使用TreeSet要点:
-
由于是二叉树,需要对元素做内部排序。所以要么实现Comparable接口,要么实现Comparator接口,否则会抛出java.lang.ClassCastException异常。
-
TreeSet中不能放入null元素,否则抛出java.lang.NullPointerException异常。
ps:如需最新的免费文档资料和教学视频,请添加QQ群(627407545)领取。