Java比较器:Comparable接口和Comparator接口

引言

在Java中,我们使用>、< 、>=、<=等比较运算符来对基本数据类型进行比较。如果我们想要对对象数组进行排序,即比较不同对象的大小,我们需要用到Java比较器。Java实现对象排序的方式有两种:

  • 自然排序:java.lang.Comparable
  • 定制排序:java.util.Comparator

String对象的比较

String类型的对象数组可以直接使用Arrays.sort()进行排序,因为String类实现了Comparable接口,重写了compareTo()方法。两个字符串比较大小可以使用compareTo()方法。

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] s = new String[]{"aa","cc","mm","gg","bb"};
        Arrays.sort(s);
        System.out.println(Arrays.toString(s)); //[aa, bb, cc, gg, mm]
        System.out.println("yy".compareTo("xy")); //1
        System.out.println("yy".compareTo("yy")); //0
        System.out.println("xxx".compareTo("xy")); //-1
    }
}

对于自定义类来说,如果需要排序,我们可以用相同的方法,即让自定义类实现Comparable接口,重写compareTo()方法。

问题一

需求:自定义一个学生类,属性包含年龄,身高,姓名。按照年龄,身高,姓名的顺序对一个学生数组进行从小到大的排序
思路:我们可以让Student类实现Comparable接口,并重写compareTo()方法。

Comparable接口

重写compareTo(Object obj)的规则

从小到大排时:

  • 如果当前对象this大于形参对象obj,则返回正整数(一般是1)。
  • 如果当前对象this等于形参对象obj,则返回0。
  • 如果当前对象this小于形参对象obj,则返回负整数(一般是-1)。

从大到小排时相反。

具体实现

首先创建Student类,实现Comparable接口并重写compareTo()方法。

//Student.java
public class Student implements Comparable {
    //这里为了方便,不将属性设置为private,省写set和get方法
    int age;
    int height;
    String name;
    Student(int age, int height, String name) {
        this.age = age;
        this.height = height;
        this.name = name;
    }

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

    @Override
    public int compareTo(Object o) {
         if (this == o) return 0;
         if (o instanceof Student) {
             Student s = (Student)o;
             //先比较age
             if (this.age > s.age) {
                 return 1;
             } else if (this.age < s.age) {
                 return -1;
             } else {
                 //age相同时比较height
                 if (this.height > s.height) {
                     return 1;
                 } else if (this.height < s.height) {
                     return -1;
                 } else {
                     //height也相同时比较name
                     return this.name.compareTo(s.name);
                 }
             }
         }
         throw new RuntimeException("传入的类型不是Student,无法比较");
    }
}

创建几个学生对象测试

//Main.java
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Student[] s = new Student[5];
        s[0] = new Student(12,140,"wang");
        s[1] = new Student(10,145,"zhao");
        s[2] = new Student(15,142,"liu");
        s[3] = new Student(12,140,"qian");
        s[4] = new Student(12,138,"sun");
        Arrays.sort(s);
        System.out.println(Arrays.toString(s));
        //排序结果
        //[Student{age=10, height=145, name="zhao"},
        // Student{age=12, height=138, name="sun"},
        // Student{age=12, height=140, name="qian"},
        // Student{age=12, height=140, name="wang"},
        // Student{age=15, height=142, name="liu"}]
    }
}

问题二

当有以下两种场景,Comparable接口不适用:

  • 当对象所在的类中没有实现Comparable接口,并且又不方便修改这个类的代码。
  • 当对象所在类中实现了Comparable接口,但是它所定义的排序规则不是你想要的。比如String类中已经实现了Comparable接口,重写了compareTo()方法,但是它所定义的是字符串从小到大排列。如果我现在想让字符串从大到小排列呢?

这时我们就需要用到Comparator接口,去实现compare()方法。

Comparator接口

重写compare(Object o1, Object o2)方法规则

从小到大排时:

  • 如果o1大于o2,返回正整数。
  • 如果o1等于o2,返回负整数。
  • 如果o1小于o2,返回负整数。

从大到小排时相反。

实现一

让字符串从大到小排列。

//Main.java
import java.util.Arrays;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        String[] s = new String[]{"aa", "cc", "mm", "gg", "bb"};
        Arrays.sort(s, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return -o1.compareTo(o2);
            }
        });
        System.out.println(Arrays.toString(s)); //[mm, gg, cc, bb, aa]
    }
}

实现二

不改变Student.java的情况下,将学生按照年龄,身高,名字降序排列。

//Main.java
import java.util.Arrays;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        Student[] s = new Student[5];
        s[0] = new Student(12,140,"wang");
        s[1] = new Student(10,145,"zhao");
        s[2] = new Student(15,142,"liu");
        s[3] = new Student(12,140,"qian");
        s[4] = new Student(12,138,"sun");
        Arrays.sort(s, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                if (o1 == o2) return 0;
                if (o1.age > o2.age) {
                    return -1;
                } else if (o1.age < o2.age) {
                    return 1;
                } else {
                    if (o1.height > o2.height) {
                        return -1;
                    } else if (o1.height < o2.height) {
                        return 1;
                    } else {
                        return -o1.name.compareTo(o2.name);
                    }
                }
            }
        });
        System.out.println(Arrays.toString(s));
        //排序结果
        //[Student{age=15, height=142, name="liu"}, 
        // Student{age=12, height=140, name="wang"}, 
        // Student{age=12, height=140, name="qian"}, 
        // Student{age=12, height=138, name="sun"}, 
        // Student{age=10, height=145, name="zhao"}]
    }
}

总结

  1. Comparable接口的方式一旦一定,保证Comparable接口的实现类的对象在任何位置都可以比较大小。
  2. Comparator接口相当于临时性的比较。

参考资料

[1] 宋红康.Java比较器概述.哔哩哔哩
[2] 宋红康.Comparable自然排序举例.哔哩哔哩
[3] 宋红康.自定义类实现Comparable自然排序.哔哩哔哩
[4] 宋红康.使用Comparator实现定制排序.哔哩哔哩

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值