目录
4.1 Integer和String对象都可以进行默认的TreeSet排序
1.背景介绍
- TreeSet底层数据结构采用红黑树(平衡二叉树)来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造)。
-
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
- 基于
TreeMap
的NavigableSet
实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的Comparator
进行排序,具体取决于使用的构造方法。 -
此实现为基本操作(
add
、remove
和contains
)提供受保证的 log(n) 时间开销。
2.TreeSet保证元素唯一和排序的原理
TreeSet集合底层数据结构是红黑树(平衡二叉树)。 第一个元素存储的时候,直接作为根节点。 从第二个开始,每个元素从根节点开始比较:
大——就作为右孩子 (保证有序)
小——就作为左孩子 (保证有序)
相等——不做处理(保证唯一性)
3.两种排序(自然排序,比较器排序)
- 自然排序:让元素所属的类中实现自然排序接口Comparerable接口 。可以在自定义类中实现Comparerable接口,重写compareTo()方法。
- 比较器排序:让集合的构造方法接收一个比较器Comparator接口的子类对象。可以在自定义类中实现Comparetor接口,重写compare()方法。
4.自然排序的实现
4.1 Integer和String对象都可以进行默认的TreeSet排序
public class TEST {
public static void main(String[] args) {
//Integer类本身已经实现了Comparable接口,重新了compare()方法。
TreeSet<Integer> treeSet = new TreeSet<Integer>(); // 使用自然排序
treeSet.add(10);
treeSet.add(10);
treeSet.add(20);
treeSet.add(18);
treeSet.add(14);
treeSet.add(14);
treeSet.add(15);
treeSet.add(90);
treeSet.add(1);
for (Integer number : treeSet) {
System.out.println(number); //输出结果: 1 10 14 15 18 20 90 (去重且排序)
}
}
}
4.2 对自定义对象进行 自然排序
自然排序要进行一下操作:
1.自定义类中实现 Comparable<T>接口
2.重写Comparable接口中的compareTo方法
// Student类,实现Comparable接口
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student(){
super();
}
public Student(String name, int age){
super();
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;
}
// 重写compareTo方法
@Override
public int compareTo(Student s) {
//return -1; //-1表示放在红黑树的左边,即逆序输出
//return 1; //1表示放在红黑树的右边,即顺序输出
//return 0; //表示元素相同,仅存放第一个元素
//主要条件 姓名的长度,如果姓名长度小的就放在左子树,否则放在右子树
int num = this.name.length() - s.name.length();
//姓名的长度相同,不代表内容相同,如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
//如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
//如果这两个字符串相等,则结果为 0
int num1 = num == 0 ? this.name.compareTo(s.name) : num; // this.name.compareTo(s.name) 这个compareTo(String str)方法已经重写,可以直接调用。
//姓名的长度和内容相同,不代表年龄相同,所以还要判断年龄
int num2 = num1 == 0 ? this.age - s.age : num1;
return num2;
}
}
// 测试类
import java.util.*;
public class TEST {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts=new TreeSet<Student>();
//创建元素对象
Student s1=new Student("zhangsan",20);
Student s2=new Student("lis",22);
Student s3=new Student("wangwu",24);
Student s4=new Student("chenliu",26);
Student s5=new Student("zhangsan",22);
Student s6=new Student("qianqi",24);
//将元素对象添加到集合对象中
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历
for(Student s:ts){
System.out.println(s.getName()+"-----------"+s.getAge());
}
}
}
//打印:
lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22
5.比较器排序的实现
比较器排序步骤:
1.单独创建一个比较类,这里以MyComparator为例,并且要让其继承Comparator<T>接口
2.重写Comparator接口中的compare()方法
5.1 一般比较器写法
// 自定义类: Student类
public class Student {
private String name;
private int age;
public Student(){
super();
}
public Student(String name, int age){
super();
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;
}
}
// 单独创建的一个比较类,继承Comparator<T>接口,并重写compare()方法
import java.util.Comparator;
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1,Student s2) {
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
}
// 测试类
import java.util.*;
public class TEST {
public static void main(String[] args) {
//创建集合对象
//TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
TreeSet<Student> ts=new TreeSet<Student>(new MyComparator());
//创建元素对象
Student s1=new Student("zhangsan",20);
Student s2=new Student("lis",22);
Student s3=new Student("wangwu",24);
Student s4=new Student("chenliu",26);
Student s5=new Student("zhangsan",22);
Student s6=new Student("qianqi",24);
//将元素对象添加到集合对象中
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
//遍历
for(Student s:ts){
System.out.println(s.getName()+"-----------"+s.getAge());
}
}
}
// 打印结果:
lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22
5.2 常见比较器写法
单独创建一个MyComparator类不是特别好,可以将MyComparetor的内容直接写到主类中。开发中常用匿名内部类实现。
public class TreeSetDemo {
public static void main(String[] args) {
// 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
// 而匿名内部类就可以实现这个东西
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 姓名长度
int num = s1.getName().length() - s2.getName().length();
// 姓名内容
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
// 年龄
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
return num3;
}
});
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
感谢:
https://blog.csdn.net/jinhuoxingkong/article/details/51191106