深度解析Java中的Comparable接口和Comparator接口

 大家好,我是小鱼儿

新的一天,大家一起加油!

深度解析Java中的Comparable接口和Comparator接口_第1张图片

目录

引子

Comparable接口

Comparator接口 


引子

我们之前的文章的文章提到了Arrays是一个数组工具类,用Arrays.sort能够对各种类型的数组进行排序,那下面的数组能不能用Arrays.sort排序呢?

class Student {  // 自定义的学生类
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override  // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值
    public String toString() { 
        // 这是IDEA的自动生成的toString重写,你也可以按照自己的喜好自由的选择打印的形式
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[] { // 我们定义了一个数组,数组中每个元素都是一个学生对象
              new Student("zhangsan", 13),
              new Student("lisi", 23),
              new Student("able", 17),
        };
        Arrays.sort(students); // 用类Arrays.sort对students数组进行排序
        System.out.println(Arrays.toString(students));
        // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述
    }
}

但你运行的时候,你会发现程序报错了

深度解析Java中的Comparable接口和Comparator接口_第2张图片

 

程序抛出了异常,你说不对呀!我的Arrays不是对任意的数组都可以进行操作吗?之前我们就用Arrays.toString成功打印了数组元素都是对象的一个数组呀!

但是朋友,你告诉Arrays拿什么排了吗?是按姓名 还是按年龄呢? 

所以我们要告诉编译器那什么来排序,而这就要用到我们的Comparable接口

 

Comparable接口

通过参考手册我们可以看到

深度解析Java中的Comparable接口和Comparator接口_第3张图片

 

 我们的程序就可以写成这样

//Comparable接口在java.lang这个包下面,而java.lang这个包由java解释器自动引入,而不用import语句引入
class Student implements Comparable{  // 实现Comparable接口,注意Comparable后要加上要比较的对象的类型
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override  // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值
    public String toString() {
        return "[" + this.name + ":" + this.age + "]";
    }

    @Override // 实现Comparable接口中的中的compareTo方法
    public int compareTo(Student o) {
        if (this.age > o.age) return 1;  // this.代表对当前对象的引用,o.代表对参数对的引用
        else if (this.age < o.age) return -1;
        else return 0;
    }
    //如果当前对象应排在参数对象之前, 返回小于 0 的数字;
    //比如上面我们如果this.age>o.age,那么返回1,就this.age在o.age的后面,即按年龄升序排列
    //如果当前对象应排在参数对象之后, 返回大于 0 的数字;
    //如果当前对象和参数对象不分先后, 返回 0;
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[] { // 我们定义了一个数组,数组中每个元素都是一个学生对象
              new Student("zhangsan", 13),
              new Student("lisi", 23),
              new Student("able", 17),
        };
        Arrays.sort(students); // 用类Arrays.sort对students数组进行排序
        System.out.println(Arrays.toString(students));
        // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述
    }
}

看一下输出结果

深度解析Java中的Comparable接口和Comparator接口_第4张图片

 

咱们再回过头来看看为为啥必须要实现接口中的 compareTo 方法,

深度解析Java中的Comparable接口和Comparator接口_第5张图片上图中的这个(Comparable)a[runHi++]数组就是我们Arrays.sort传过去的学生对象数组,下图中的this指的是谁?就要看谁此时调用了compareTo方法

上图中compareTo(a[lo])点号前面的的学生对象a[runHi++]就是此时的this。下图中的o对象就是上图中的compareTo(a[lo]中的a[lo]学生对象

深度解析Java中的Comparable接口和Comparator接口_第6张图片

 

不知道你理解了吗?如果没理解,我们不妨再举一个栗子 

深度解析Java中的Comparable接口和Comparator接口_第7张图片

 

总结一下

 我们知道在Arrays.sort(students); 中是传了一个学生对象数组,如上图所示在调用Arrays对对象数组排序的时候,其实就调用了我们的Comparable接口中的compareTo方法对数组的每个元素进行了排序

其实在Java中对于引用数据类型的比较或者排序,一般都要用到使用Comparable接口中的compareTo() 方法

 

那如何按年龄排序呢?一样的道理(只是发生了一些小小的变化)

//Comparable接口在java.lang这个包下面,而java.lang这个包由java解释器自动引入,而不用import语句引入
class Student implements Comparable{  // 实现Comparable接口,注意Comparable后要加上要比较的对象的类型
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override  // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值
    public String toString() {
        return "[" + this.name + ":" + this.age + "]";
    }

    @Override // 实现Comparable接口中的中的compareTo方法
    public int compareTo(Student o) {
        // 为啥在compareTo里还要用compareTo,因为name是个String类型也是个引用类型,也需要用compareTo比较, 不过这里用到的compareTo方法是String类里重写的
        if (this.name.compareTo(o.name) > 0) return 1;  // this.代表对当前对象的引用,o.代表对参数对的引用
        else if (this.name.compareTo(o.name) < 0) return -1;
        else return 0;
    }
    //如果当前对象应排在参数对象之前, 返回小于 0 的数字;
    //如果当前对象应排在参数对象之后, 返回大于 0 的数字;
    //如果当前对象和参数对象不分先后, 返回 0;
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[] { // 我们定义了一个数组,数组中每个元素都是一个学生对象
              new Student("zhangsan", 13),
              new Student("lisi", 23),
              new Student("able", 17),
        };
        Arrays.sort(students); // 用类Arrays.sort对students数组进行排序
        System.out.println(Arrays.toString(students));
        // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述
    }
}

深度解析Java中的Comparable接口和Comparator接口_第8张图片

bi 

但这样会有一个问题,那就是一但写好了排序的方法,那就一下子就写死了。你在compareTo方法里实现的是按年龄比较,那你接下来就只能按年龄来比较。如果想按姓名来比较,那只能够在重写compareTo方法。 

那有什么办法能够解决这个问题呢?

有!那就是我们接下来要讲的比较器:Comparator接口 


Comparator接口

老规矩我们先来了解一下Comparator这个接口

深度解析Java中的Comparable接口和Comparator接口_第9张图片

所以就像Comparable 接口一样,我们只要实现了Comparator接口,并重写Comparator里的compare方法就可以实现对学生对象数组的排序

比如我们上面的年龄比较就可以写成这样

class Student { 
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override  // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值
    public String toString() {
        return "[" + this.name + ":" + this.age + "]";
    }
}
class AgeComparator implements Comparator { // 年龄比较器
    @Override  // 实现Comparator接口中的compare方法
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    // 反正返回的也是数字,当o1.age>o2.age时返回大于零的数,即o1对象排在o2对象的后面,升序排列,我们之前用Comparable接口时也可以这样简写
    }
    //如果当前对象应排在参数对象之前, 返回小于 0 的数字;
    //如果当前对象应排在参数对象之后, 返回大于 0 的数字;
    //如果当前对象和参数对象不分先后, 返回 0;
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[]{ // 我们定义了一个数组,数组中每个元素都是一个学生对象
                new Student("zhangsan", 13),
                new Student("lisi", 23),
                new Student("able", 17),

        };
        AgeComparator ageComparator = new AgeComparator(); // 对年龄比较器进行实例化
        Arrays.sort(students, ageComparator); 
        // 用类Arrays.sort对students数组进行排序,这里传了两个参数(学生对象和所对应的年龄比较器)
        System.out.println(Arrays.toString(students));
        // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述
    }
}

深度解析Java中的Comparable接口和Comparator接口_第10张图片

 

Arrays.sort此时是一个传两个参数的方法: 

深度解析Java中的Comparable接口和Comparator接口_第11张图片

 那姓名比较比较自然就出来了

class Student {
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override  // 对父类Object的父类方法进行重写,以便直接打印出当前对象的值
    public String toString() {
        return "[" + this.name + ":" + this.age + "]";
    }
}
class NameComparator implements Comparator { // 姓名比较器
    @Override  // 实现Comparator接口中的compare方法
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name); 
// 因为name是String类型,也是一个引用类型,也要用到compareTo方法,此时的compareTo方法是String类里重写的方法
    }
    //如果当前对象应排在参数对象之前, 返回小于 0 的数字;
    //如果当前对象应排在参数对象之后, 返回大于 0 的数字;
    //如果当前对象和参数对象不分先后, 返回 0;
}
public class test4 {
    public static void main(String[] args) {
        Student[] students = new Student[]{ // 我们定义了一个数组,数组中每个元素都是一个学生对象
                new Student("zhangsan", 13),
                new Student("lisi", 23),
                new Student("able", 17),

        };
        NameComparator nameComparator = new NameComparator();
        Arrays.sort(students, nameComparator); // 用类Arrays.sort对students数组进行排序
        System.out.println(Arrays.toString(students));
        // 我们前几篇文章专门对Arrays以及toString方法的重写做了说明,这里不再赘述
    }
}

 

总结一下就是:

Comparable接口和Comparator接口都是Java中用来比较和排序引用类型数据的接口,要实现比较,就需要实现他们所各种对应的compareTo方法或者compare方法。

不同的是:Comparator使用起来更加灵活,所以我更倾向于使用比较器:Comparator

 

好了,今天我们就聊到这里,咱们下篇博客见 

深度解析Java中的Comparable接口和Comparator接口_第12张图片

 

你可能感兴趣的:(JavaSE,数组排序,Java基础,Java中重要的接口)