TreeSet 集合
一、API文档中的TreeSet集合
- 使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
二、TreeSet集合的特性
- 元素唯一
- 可以排序(根据创建TreeSet集合时使用的构造器选择以下两种方法之一)
- 自然排序
- 比较器(Comparator)排序
三、TreeSet集合的自然排序
- 构造方法:TreeSet()
- 构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
package com.treeset.demo;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> treeset = new TreeSet<Integer>();
treeset.add(10);
treeset.add(8);
treeset.add(11);
treeset.add(3);
treeset.add(1);
treeset.add(9);
for (Integer integer : treeset) {
System.out.println(integer);
}
}
}
***执行结果:***
1
3
8
9
10
11
四、TreeSet集合的数据结构
- 数据结构:红黑树(一种自平衡的二叉树)
- 参考文章:https://www.cnblogs.com/yinbiao/p/10732600.html
五、TreeSet集合自然排序原理
(五~十将完整展现TreeSet集合自然排序的原理和方法实现)
- 当添加第一个元素进TreeSet集合中时,把这个元素作为根节点
- 当后面再有元素插入进来时,就会和节点进行比较
- 大的放右边
- 小的放左边
- 如果一样就不添加(去重)
- 按照一种特殊的遍历树的方式进行遍历,得到的结果就是有序的,遍历二叉树的方式有三种:
- 前序遍历
- 中序遍历
- 后序遍历
- 这里按照前序遍历的方式就能使得到的结果是有序的
- 若二叉树为空则结束返回,否则:
- ① 访问根结点
- ② 前序遍历左子树
- ③ 前序遍历右子树
- 需要注意的是:遍历左右子树时仍然采用前序遍历方法
六、案例实操一:实现学生类对象
//Student.java
package com.treeset.demo;
public class Student {
private String name;
private int age;
public Student(String name, int i) {
super();
this.name = name;
this.age = i;
}
public Student() {
super();
}
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 "Student [name=" + name + ", age=" + age + "]";
}
}
//TreeSetTest2.java
package com.treeset.demo;
import java.util.TreeSet;
public class TreeSetTest2 {
public static void main(String[] args) {
TreeSet<Student> treeset = new TreeSet<Student>();
Student stu1 = new Student("张三", 18);
Student stu2 = new Student("李四", 19);
Student stu3 = new Student("王五", 20);
treeset.add(stu1);
treeset.add(stu2);
treeset.add(stu3);
}
}
- 执行后出现类型转换异常的报错:
- Exception in thread “main” java.lang.ClassCastException: class com.treeset.demo.Student cannot be cast to class java.lang.Comparable
- Student类无法转换成lang包下的Comparable接口
七、Comparable接口
- 此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
- 因此,只有实现了该接口的对象才算是具有自然排序的特性
- 上面第三大点(TreeSet集合的自然排序)中的代码可以进行自然排序的原因是Integer类已经实现了Comparable接口,从而使其对象具有可比较性
八、Comparable接口的方法
- int compareTo(T o) :比较此对象与指定对象的顺序
对象是如何通过Comparable接口的compareTo(T)方法进行排序的?
- 查看Integer的源码中的compareTo方法以及compare方法
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
- x(this.value)为当前对象,y(anotherInteger.value)为被比较的对象
- compare方法比较时会产生三种情况:
- x==y 返回0
- x<y 返回-1(存储在树节点的左边)
- x>y 返回1(存储在树节点的右边)
public static void main(String[] args) {
Integer i1 = 10;
Integer i2 = 20;
Integer i3 = 20;
System.out.println(i1.compareTo(i2)); // -1
System.out.println(i2.compareTo(i1)); // 1
System.out.println(i2.compareTo(i3)); // 0
}
九、案例实操二:向TreeSet集合添加字符串对象
package com.treeset.demo;
import java.util.TreeSet;
public class TreeSetTest4 {
public static void main(String[] args) {
TreeSet<String> treeset = new TreeSet<String>();
treeset.add("abcde");
treeset.add("fghij");
treeset.add("fghij");
treeset.add("faaaa");
treeset.add("klmno");
for (String str : treeset) {
System.out.println(str);
}
}
}
***执行结果:***
abcde
faaaa
fghij
klmno
- String类有自己的compareTo方法按照字典顺序来排序
十、如何让案例实操一的Student对象具有可比较性
- Student类实现Comparable接口
- 重写compareTo()方法
- 完整代码:
//Student.java
package com.treeset.demo2;
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int i) {
super();
this.name = name;
this.age = i;
}
public Student() {
super();
}
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 "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Student s) {
//主要比较条件:年龄
int num = this.age - s.getAge();
//次要比较条件:名字
int num2 = num==0 ? this.name.compareTo(s.getName()) : num;
return num2;
}
}
//TreeSetTest.java
package com.treeset.demo2;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<Student>();
Student stu1 = new Student("张三", 19);
Student stu2 = new Student("李四", 19);
Student stu3 = new Student("王五", 20);
set.add(stu1);
set.add(stu2);
set.add(stu3);
for (Student stus : set) {
System.out.println(stus);
}
}
}
***执行结果:***
Student [name=张三, age=19]
Student [name=李四, age=19]
Student [name=王五, age=20]
十一、使用比较器对TreeSet集合进行排序
- 构造方法:TreeSet(Comparator<? super E> comparator)
- 构造一个新的空 TreeSet,它根据指定比较器进行排序
- API文档中的Comparator接口
- 利用TreeSet集合的比较器进行比较排序的步骤:
- 创建比较器对象(实现Comparator接口的对象)
- 使用带有比较器的TreeSet构造方法创建集合
- 完整代码:
//Student.java
package com.treeset.demo3;
public class Student {
private String name;
private int age;
public Student(String name, int i) {
super();
this.name = name;
this.age = i;
}
public Student() {
super();
}
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 "Student [name=" + name + ", age=" + age + "]";
}
}
//MyComparator.java
package com.treeset.demo3;
import java.util.Comparator;
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
//主要比较条件:年龄
int num1 = s1.getAge() - s2.getAge();
//次要比较条件:名字
int num2 = num1==0 ? s1.getName().compareTo(s2.getName()) : num1;
return num2;
}
}
//TreeSetTest.java
package com.treeset.demo3;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
//创建比较器对象
Comparator<Student> comparator = new MyComparator();
TreeSet<Student> set = new TreeSet<Student>(comparator);
Student stu1 = new Student("张三", 18);
Student stu2 = new Student("李四", 20);
Student stu3 = new Student("王五", 20);
set.add(stu1);
set.add(stu2);
set.add(stu3);
for (Student stus : set) {
System.out.println(stus);
}
}
}
***执行结果:***
Student [name=张三, age=18]
Student [name=李四, age=20]
Student [name=王五, age=20]
十二、匿名内部类实现比较器对象
TreeSet<Student> set = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num1 = s1.getAge() - s2.getAge();
int num2 = num1==0 ? s1.getName().compareTo(s2.getName()) : num1;
return num2;
}
});