欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

TreeSet集合的理解(自然排序和比较器排序)

程序员文章站 2022-07-09 22:35:20
...

TreeSet集合

TreeSet集合是Set集合的一个子实现类,它是基于TreeMap中的NavigableSet接口实现的

TreeSet集合是默认通过自然排序将集合中的元素进行排序

TreeSet有两种排序方式:

1)自然排序

2)比较器排序

让我们先来看看一个例题:

package com.TreeSetDome;
 
import java.util.TreeSet;
 
public class TreeSetDome {
 
public static void main(String[] args) {
TreeSet<Integer> set=new TreeSet<Integer>();
set.add(17);
set.add(25);
set.add(23);
set.add(14);
set.add(17);
set.add(30);
for(Integer s:set) {
System.out.println(s);
}
}
}
运行结果:
14
17
23
25
30

根据上述结果可以看出TreeSet集合是自然排序和去重的,为什么会达到这样的效果呢?

TreeSet集合的无参构造就是属于自然排序

TreeSet<Integer> set=new TreeSet<Integer>();

这是因为TreeSet集合依赖于TreeMap的红黑树结构实现的,下面让我们根据上述例题去看看红黑树结构的理解:

set.add(17);

set.add(25);

set.add(23);

set.add(14);

set.add(17);

set.add(30);

17先进行存储,所以将17作为根节点,与后面的元素进行比较,25进来后与17相比,比17大,所以成为17的右孩子,放在17的右边,23进来比17大所以要放在17的右边,但是和25比较比他小,所以放在25的左边,接下来1417小放在17的左边,17进来与17的值一样不理睬,继续下个30,比17大比25大,放在右边25 的右边,绘成图就是二叉图方式,结构一定是自平衡的

使用TreeSet进行自定义函数的排序,对年龄由小到大进行排序

package com.TreeSetDome;
 
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;
}
 
//因为上面Student类实现了comparable接口,所以必须重写comparaTo方法才能达到排序的效果
@Override
public int compareTo(Student s) {
// return 0;
/**
 * 因为这是我们自定义的类,系统并没有告诉我们如何进行排序
 * 所以需要我们自己手动进行排序
 * 需求:按年龄由小到大进行排序
 */
//年龄进行排序,由小到大
int num=this.age-s.age;
//当年龄大小相等时,比较名字
int num2=num==0?this.name.compareTo(s.getName()):num;
return num2;
}

}

 

package com.TreeSetDome;
 
import java.util.TreeSet;
 
public class StudentDome {
 
public static void main(String[] args) {
//创建TreeSet集合对象
TreeSet<Student> set=new TreeSet<Student>();
//创建学生对象,这里的学生姓名不要写成汉字,因为每个汉字的字节大小不一样
Student s1=new Student("dilireba",27);
Student s2=new Student("gaowen",25);
Student s3=new Student("zhaoxingxing",24);
Student s4=new Student("wuxuanyi",23);
Student s5=new Student("dilireba",27);
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
//增强for循环
for(Student st:set) {
System.out.println(st.getName()+"---"+st.getAge());
}
}
}
运行结果:
wuxuanyi---23
zhaoxingxing---24
gaowen---25
dilireba---27

由上例可以看出我们在学生类上实线了comparable接口,并且在学生类中重写了comparaTo方法,当我们没有进行以上的这些操作时,运行时就会出现这样的错误,

Exception in thread "main" java.lang.ClassCastException: com.TreeSetDome.Student cannot be cast to java.lang.Comparable

因为没有实现comparable接口,所以会出现以上的这个异常,但是为什么在前面添加数字的时候并不需要实现comparable接口呢?这是添加数字时我们确定了类型为Integer类型,它本身就已经实现了comparable接口,不需要我们再去添加,具体可以去API中观看,所以今后在使用TreeSet创建自定义类排序的时候,一定要自己去实现comparable接口,和重写comparaTo方法

上述中我们重写的ComparaTo方法是按照年龄来排序的,接下来让我们按照姓名的长度以及年龄的大小来排序:

@Override
public int compareTo (Student s) {
/**
 * 因为这是我们自定义的类,系统并没有告诉我们如何进行排序
 * 所以需要我们自己手动进行排序
 * 需求:按姓名的长度来排序,然后再以年龄的大小来排序
 */
//按姓名的长短来排序,由小到大排序
int num=this.getName().length()-s.getName().length();
//再去比较的姓名的内容是否一致
int num2=num==0?this.getName().compareTo(s.getName()):num;
//名字一致,有时候并不是同一个人 还得再去比较年龄的大小
int  num3=num2==0?this.age-s.age:num2;
return num3;
这是重写的comparaTo方法,我新添加了一个学生变量Student s6=new Student("dilireba",25);
 
运行结果:
gaowen---25
dilireba---25
dilireba---27
wuxuanyi---23
zhaoxingxing---24

上面我们介绍了自然排序法,自然排序法主要就是运用TreeSet的无参构造,通过实现comparable接口中的comparaTo方法去进行自然排序,接下来让我们看看比较器排序,看看二者的不同

package com.TreeSet;
 
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;
}

}

 

 

package com.TreeSet;
 
import java.util.Comparator;
import java.util.NavigableMap;
 
public class ComparatorDome implements Comparator<Student>{
 
@Override
/**
 * 因为测试类中TreeSet集合的引用参数是接口,所以需要创建这个子实现类去实现这个接口
 * 这里的s1就相当于自然排序中的this,s2相当于s
 * 先按名字的长度,当长度一致时,再按年龄的大小进行排序
 */
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;
}
 
}

 

 

package com.TreeSet;
 
import java.util.TreeSet;
 
public class StudentDome {
 
 
public static void main(String[] args) {
//创建TreeSet集合对象,运用比较器排序
//Comparator是一个接口,所以我们得创建一个子实现类去实现它
TreeSet<Student> set=new TreeSet<Student>(new ComparatorDome());
//创建学生对象
Student s1=new Student("dilireba",27);
Student s2=new Student("gaowen",25);
Student s3=new Student("zhaoxingxing",24);
Student s4=new Student("wuxuanyi",23);
Student s5=new Student("dilireba",25);
Student s6=new Student("dilireba",25);
//将学生对象添加到集合中
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
//增强for循环遍历
for(Student s:set) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
运行结果:
gaowen---25
dilireba---25
dilireba---27
wuxuanyi---23
zhaoxingxing---24

除了创建子实现类去实现Comparator接口,我们还可以在测试类中通过匿名内部类的方式去测试,就不用单独去创建一个子实现类了

在这里学生类我就不写了,上面有,直接写测试类中的匿名内部类了

package com.TreeSet;
 
import java.util.Comparator;
import java.util.TreeSet;
 
public class StudentDome {
 
 
public static void main(String[] args) {
//创建TreeSet集合对象,运用比较器排序
//Comparator是一个接口,所以我们得创建一个子实现类去实现它
//匿名内部类的使用
TreeSet<Student> set=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("dilireba",27);
Student s2=new Student("gaowen",25);
Student s3=new Student("zhaoxingxing",24);
Student s4=new Student("wuxuanyi",23);
Student s5=new Student("dilireba",25);
Student s6=new Student("dilireba",25);
//将学生对象添加到集合中
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
//增强for循环遍历
for(Student s:set) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
运行结果:
gaowen---25
dilireba---25
dilireba---27
wuxuanyi---23
zhaoxingxing---24