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的左边,接下来14比17小放在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