我们知道基本类型中byte、char、short、int、float、long、double都可以比较大小,而他们比较大小的方式是直接使用">","<"和"=",那我们如何去比较指定对象的大小呢?这就用到了我们要说的比较器,比较器分为Comparable和Comparator两种,下面详细介绍这两种比较器。

Comparable比较器

要使用Comparable比较器需要实现Comparable接口,Comparable接口只有一个方法,即

public int compareTo(T o);

该方法返回一个整数,返回值>0时,当前对象>给定对象;返回值=0时,当前对象=给定对象;返回值<0时,当前对象<给定对象。至于方法内部怎么实现则由具体业务而定,例如我们可以看一下Integer的compareTo()方法的实现:

public int compareTo(Integer anotherInteger) {
    return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

可以看到Integer比较大小其实就是比较其value属性的大小。类似的,我们需要为我们的类实现排序时,也是根据业务场景对类属性做判断并返回结果。例如,我们需要实现一个小学生类,小学生只学语文跟数学。考试结束后要根据考试成绩排名,排名第一的小学生送大红花,对成绩进行比较时,数学分数更高的则认为成绩更好,数学成绩一样再比较语文。

对了,一般情况下,对指定的对象集合或数组进行排序使用Collections.sort()方法或者Arrays.sort()方法,这两个方法最终也是调用的对象的compareTo()方法。需要注意的是,使用这两个方法时该类必须实现Comparable接口,因为方法签名中指定了泛型的上界为Comparable接口。

小学生类:

public PrimaryStudent(String name, int mathScore, int chineseScore) {
        this.name = name;
        this.mathScore = mathScore;
        this.chineseScore = chineseScore;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMathScore() {
        return mathScore;
    }

    public void setMathScore(int mathScore) {
        this.mathScore = mathScore;
    }

    public int getChineseScore() {
        return chineseScore;
    }

    public void setChineseScore(int chineseScore) {
        this.chineseScore = chineseScore;
    }

    @Override
    public String toString() {
        return "PrimaryStudent{" +
                "name='" + name + '\'' +
                ", mathScore=" + mathScore +
                ", chineseScore=" + chineseScore +
                '}';
    }

    @Override
    public int compareTo(PrimaryStudent o) {
        //两个小学生的数学成绩相等则返回语文成绩的比较结果
        if (this.mathScore == o.getMathScore()){
            return this.chineseScore - o.getChineseScore();
        } else {
            //数学成绩不相等则直接返回数学成绩的比较结果
            return this.mathScore - o.getMathScore();
        }
    }
}

测试程序:

public static void main(String[] args) {
    List<PrimaryStudent> primaryStudentList = new ArrayList<>();
    primaryStudentList.add(new PrimaryStudent("jenny",95,28));
    primaryStudentList.add(new PrimaryStudent("danny",90,30));
    primaryStudentList.add(new PrimaryStudent("LiMing",95,32));
    System.out.println("********排名之前的成绩********");
    for (PrimaryStudent ps:primaryStudentList) {
        System.out.println(ps);
    }
    Collections.sort(primaryStudentList);
    System.out.println("********排名之后的成绩********");
    for (PrimaryStudent ps:primaryStudentList) {
        System.out.println(ps);
    }
}

输出结果:

********排名之前的成绩********
PrimaryStudent{name='jenny', mathScore=95, chineseScore=28}
PrimaryStudent{name='danny', mathScore=90, chineseScore=30}
PrimaryStudent{name='LiMing', mathScore=95, chineseScore=32}
********排名之后的成绩********
PrimaryStudent{name='danny', mathScore=90, chineseScore=30}
PrimaryStudent{name='jenny', mathScore=95, chineseScore=28}
PrimaryStudent{name='LiMing', mathScore=95, chineseScore=32}

可以看到的确是实现我们想要的效果了,先根据数学成绩升序排序再根据语文成绩升序排序,事实上这里降序排序更合理一些(在compareTo()方法中将减数和被减数互换即可),但我怕有人看起来会很懵逼,所以有兴趣的话可以自己修改代码尝试一下。

要使用Comparable比较器就必须要实现Comparable接口,这样不仅耦合度高,并且有些情况下的类我们不能修改还要实现排序功能,这个时候就可以使用Comparator比较器。

Comparator比较器

要使用Comparator比较器,也需要实现Comparator接口,但不需要我们排序的类实现,这样就降低了耦合度,并且可以根据不同的业务场景使用不同的Comparator比较器实现不同的排序。Comparator接口需要我们实现的同样只有一个方法,即

int compare(T o1, T o2);

该方法的返回值规则与Comparable比较器相同,不多赘述。依旧是上面小学生考试的业务场景,但是俩小学合并了,一个小学注重数学,一个小学注重语文,为了方便两校老师,这次我们需要实现两个Comparator比较器。第一个先比较数学成绩,数学成绩相同再比较语文成绩;第二个先比较语文成绩,语文成绩相同再比较数学成绩。

Collections.sort()和Arrays.sort()方法不仅支持只传入要排序的对象集合和数组,还支持传入指定的Comparator比较器。

第一个比较器:

class MathIsBest implements Comparator<PrimaryStudent> {

    @Override
    public int compare(PrimaryStudent o1, PrimaryStudent o2) {
        //两个小学生的数学成绩相等则返回语文成绩的比较结果
        if (o1.getMathScore() == o2.getMathScore()){
            return  o1.getChineseScore() - o2.getChineseScore();
        } else {
            //数学成绩不相等则直接返回数学成绩的比较结果
            return  o1.getMathScore() - o2.getMathScore();
        }
    }
}

第二个比较器:

class ChineseIsBest implements Comparator<PrimaryStudent> {

    @Override
    public int compare(PrimaryStudent o1, PrimaryStudent o2) {
        //两个小学生的语文成绩相等则返回数学成绩的比较结果
        if (o1.getChineseScore() == o2.getChineseScore()){
            return  o1.getMathScore() - o2.getMathScore();
        } else {
            //语文成绩不相等则直接返回语文成绩的比较结果
            return  o1.getChineseScore() - o2.getChineseScore();
        }
    }
}

测试程序:

public static void main(String[] args) {
        List<PrimaryStudent> primaryStudentList = new ArrayList<>();
        primaryStudentList.add(new PrimaryStudent("jenny",95,28));
        primaryStudentList.add(new PrimaryStudent("danny",90,30));
        primaryStudentList.add(new PrimaryStudent("LiMing",95,32));
        System.out.println("********排名之前的成绩********");
        for (PrimaryStudent ps:primaryStudentList) {
            System.out.println(ps);
        }
        scoreSort("数学",primaryStudentList);
        System.out.println("********按数学排名之后的成绩********");
        for (PrimaryStudent ps:primaryStudentList) {
            System.out.println(ps);
        }
        scoreSort("语文",primaryStudentList);
        System.out.println("********按语文排名之后的成绩********");
        for (PrimaryStudent ps:primaryStudentList) {
            System.out.println(ps);
        }
    }

    private static void scoreSort(String course,List<PrimaryStudent> primaryStudentList){
        if ("数学".equals(course)) {
            Collections.sort(primaryStudentList,new MathIsBest());
        } else if ("语文".equals(course)){
            Collections.sort(primaryStudentList,new ChineseIsBest());
        }
    }

输出结果:

********排名之前的成绩********
PrimaryStudent{name='jenny', mathScore=95, chineseScore=28}
PrimaryStudent{name='danny', mathScore=90, chineseScore=30}
PrimaryStudent{name='LiMing', mathScore=95, chineseScore=32}
********按数学排名之后的成绩********
PrimaryStudent{name='danny', mathScore=90, chineseScore=30}
PrimaryStudent{name='jenny', mathScore=95, chineseScore=28}
PrimaryStudent{name='LiMing', mathScore=95, chineseScore=32}
********按语文排名之后的成绩********
PrimaryStudent{name='danny', mathScore=95, chineseScore=28}
PrimaryStudent{name='jenny', mathScore=90, chineseScore=30}
PrimaryStudent{name='LiMing', mathScore=95, chineseScore=32}

可以看到实现了我们需要的功能。