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

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

猜你喜欢

转载自blog.csdn.net/j_better/article/details/80275852
今日推荐