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

TreeSet集合

自然排序和比较器排序

​ 当指执行插入排序、希尔排序、归并排序等算法时,比较两个对象“大小”的比较操作。我们很容易理解整型的 i>j 这样的比较方式,但当我们对多个对象进行排序时,如何比较两个对象的“大小”呢?这样的比较 stu1 > stu2 显然是不可能通过编译的。为了解决如何比较两个对象大小的问题,JDK提供了两个接口java.lang.Comparablejava.util.Comparator

TreeSet集合是Set集合的一个子实现类,它是基于TreeMap中的NavigableSet接口实现的
TreeSet集合是默认通过自然排序将集合中的元素进行排序
汉字的顺序根据Unicode 码表排序

TreeSet有两种排序方式:

  1. 自然排序:java.lang.Comparable

    • Comparable 接口中只提供了一个方法: compareTo(Object obj),该方法的返回值是 int 。如果返回值为正数,则表示当前对象(调用该方法的对象)比 obj 对象“大”;反之“小”;如果为零的话,则表示两对象相等。
  2. 比较器排序:java.util.Comparator

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

package com.xdkj.Test09;

import java.util.TreeSet;

public class TreeSetDemo {

    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.xdkj.Test09;

public class Student implements Comparable<Student> {

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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.xdkj.Test09;

import java.util.TreeSet;

public class StudentDemo {

    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.xdkj.Test10;

public class Student {

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

package com.xdkj.Test10;

import java.util.Comparator;

public class ComparatorDome implements Comparator<Student> {

    /**
     * 因为测试类中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.xdkj.Test10;

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.xdkj.Test10;

import java.util.Comparator;
import java.util.TreeSet;

/**
 * @Author:Lenvo
 * @Description:
 * @Date: 2020-12-16 16:21
 */
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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值