常用接口 - 比较Comparable、比较器Comparator

比较接口

在创建自己的数据类型时,实现了 Comparable 接口就能够保证用例代码可以将其排序 -- 算法第四版

记录的一个原因是学习的过程中用得上,另一个原因是查到的高分博客,甚至没有解释返回值的意义。。。

JDK文档

JDK文档

Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

稍微翻译

this.value - 当前对象的值

that.value - 外部的值

this.value 和 that.value 做比较:

返回的值可以是 {负整数, 0, 正整数 }

对应的情况是 this.value { 小于, 等于, 大于} that.value

Comparable.java源码

public interface Comparable<T> {
    public int compareTo(T o);
}

实现结构

如上述的分析,只要让this每个需要排序值都去做比对:

this.value[i] > that.value[i] -------> return +1;

this.value[i] = that.value[i] -------> return 0;

this.value[i] < that.value[i] -------> return -1;

即可

例如这里有一个Student类,含有 age, grade, score 三个属性

public class Student implements Comparable<Student> {
    private filnal int age;
    private filnal int grade;
    private filnal int score;
    
    ...
    
    public int compareTo (Student that) {
        if (this.age > that.age) {return 1;}
        if (this.age < that.age) {return -1;}
        if (this.grade > that.grade) {return 1;}
        if (this.grade < that.grade) {return -1;}
        if (this.score > that.score) {return 1;}
        if (this.score < that.score) {return -1;}
        return 0;
    }


}

如上述代码实现 Comparable 这一接口分为:

类主体实现Comparable<类名>

不考虑任何类型转化的比较,比较的前提都是相同的类,相同的对象

所以实现接口时,泛型为当前的类

public class Student implements Comparable<Student> {}

compareTo方法

按照规则,逐个填写即可

this.value[i] > that.value[i] -------> return +1;

this.value[i] = that.value[i] -------> return 0;

this.value[i] < that.value[i] -------> return -1;

public int compareTo (Student that) {
    if (this.age > that.age) {return 1;}
    if (this.age < that.age) {return -1;}
    if (this.grade > that.grade) {return 1;}
    if (this.grade < that.grade) {return -1;}
    if (this.score > that.score) {return 1;}
    if (this.score < that.score) {return -1;}
    return 0;
}

比较器 Comparator

Java Arrays类进行数组排序

目前学到的是应用在 Arrays.sort()中,实现对复杂类的排序

这里也以 Arrays.sort()方法为例,建立复杂数据结构并定义比较器 Comparator

待比较的Point类

按照注释的说法,排序后应该是

x越大元素越靠后,

同样x的情况下,y越小越靠后

/**
 * 假定有一个复合类型数据
 * x: 越大越好,或者说越大越靠前,比如 moneyOfPerson
 * y: 越小越好,或者说越小越靠前,比如 ageOfPerson
 * x 的优先级 >> y
 */
class Point {
    private int x;
    private int y;

    public Point() {
    }

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + (x/10000.) +
                "万, y=" + y +
                "岁}";
    }
}

初始化(构造数组)

手动构造一个需要比对y(第二元素)的情况

SortTest.java

Point[] pArray;

SortTest() {
    // 这里由于 x 变化范围太大,难以获得一个相等的情况,不利于比对 y
    // 因此手动创建一个和前一个值x相等,y不相等的 Point
    int N = 10;
    pArray = new Point[N];
    Random random = new Random();
    for (int i = 0; i < N - 1; i++) {
        pArray[i] = new Point(random.nextInt(1000000), random.nextInt(100));
    }
    pArray[N-1] = new Point(pArray[N-2].getX(), pArray[N-2].getY() - 1);

}

比较器Comparator

这里同时重写了

逆序方法 reversed()

比较方法 compare()

只要写好了 compare() 逆序甚至不需要写


class comparatorPoint implements Comparator {

    /**
     * 逆序
     * @return
     */
    @Override
    public Comparator reversed() {
        return Comparator.super.reversed();
    }

    /**
     * 升序排列
     * @param o1
     * @param o2
     * @return o1 和 o2 的比较结论
     *
     * o1.value[i] > o2.value[i] -------> return +1;
     * o1.value[i] = o2.value[i] -------> return 0;
     * o1.value[i] < o2.value[i] -------> return -1;
     */
    @Override
    public int compare(Object o1, Object o2) {
        Point t1 = (Point) o1;
        Point t2 = (Point) o2;
        if (t1.getX() == t2.getX()) {
            // x 相等时,y越小越好
            // 这里测试一下,越小越好这个含义,是不是和上述注释
            // 反着写就可以
            if (t1.getY() == t2.getY()) {return 0;}
            else if(t1.getY() < t2.getY()) {return +1;}
            else {return -1;}
        } else if (t1.getX() > t2.getX()) {return +1;}
        else {return -1;}
    }
}

案例

public static void main(String[] args) {
    SortTest sortTest = new SortTest();
    System.out.println("初始状态:");
    for (Point point : sortTest.pArray) {
        System.out.println(point);
    }
    System.out.println("================================");
    System.out.println("排序后:");

    Arrays.sort(sortTest.pArray, new comparatorPoint());
    System.out.println("正序:");
    for (Point point : sortTest.pArray) {
        System.out.println(point);
    }
    System.out.println("逆序:");
    Arrays.sort(sortTest.pArray, new comparatorPoint().reversed());
    for (Point point : sortTest.pArray) {
        System.out.println(point);
    }

输出

初始状态:
Point{x=11.353万, y=85岁}
Point{x=2.4705万, y=81岁}
Point{x=47.315万, y=9岁}
Point{x=73.8251万, y=40岁}
Point{x=19.9306万, y=46岁}
Point{x=46.6619万, y=56岁}
Point{x=82.8019万, y=98岁}
Point{x=81.8677万, y=99岁}
Point{x=35.127万, y=19岁}
Point{x=35.127万, y=18岁}
================================
排序后:
正序:
Point{x=2.4705万, y=81岁}
Point{x=11.353万, y=85岁}
Point{x=19.9306万, y=46岁}
Point{x=35.127万, y=19岁}
Point{x=35.127万, y=18岁} // 这里x相同,y越小地位越高,也就越靠后
Point{x=46.6619万, y=56岁}
Point{x=47.315万, y=9岁}
Point{x=73.8251万, y=40岁}
Point{x=81.8677万, y=99岁}
Point{x=82.8019万, y=98岁}
逆序:
Point{x=82.8019万, y=98岁}
Point{x=81.8677万, y=99岁}
Point{x=73.8251万, y=40岁}
Point{x=47.315万, y=9岁}
Point{x=46.6619万, y=56岁}
Point{x=35.127万, y=18岁}
Point{x=35.127万, y=19岁}
Point{x=19.9306万, y=46岁}
Point{x=11.353万, y=85岁}
Point{x=2.4705万, y=81岁}

Process finished with exit code 0

可见效果和设定相符

进一步简化Comparator

可以在使用时创建比较器

内部代码是一样的,注意格式

好处是这种写法 IDEA 会自动推断 Comparator 的类型

不需要做类型转化的操作

Arrays.sort(sortTest.pArray, new Comparator<Point>() {
    @Override
    public int compare(Point o1, Point o2) {
        if (o1.getX() == o2.getX()) {
            // x 相等时,y越小越好
            // 这里测试一下,越小越好这个含义,是不是和上述注释
            // 反着写就可以
            if (o1.getY() == o2.getY()) {return 0;}
            else if(o1.getY() < o2.getY()) {return +1;}
            else {return -1;}
        } else if (o1.getX() > o2.getX()) {return +1;}
        else {return -1;}
    }
});

简化验证

排序后:
正序:
Point{x=3.6459万, y=3岁}
Point{x=12.861万, y=16岁}
Point{x=24.4924万, y=65岁}
Point{x=33.8556万, y=31岁}
Point{x=37.4221万, y=86岁}
Point{x=41.7508万, y=85岁}
Point{x=41.7508万, y=84岁}
Point{x=56.8514万, y=83岁}
Point{x=72.7173万, y=77岁}
Point{x=96.9255万, y=45岁}

匿名正序验证:
Point{x=3.6459万, y=3岁}
Point{x=12.861万, y=16岁}
Point{x=24.4924万, y=65岁}
Point{x=33.8556万, y=31岁}
Point{x=37.4221万, y=86岁}
Point{x=41.7508万, y=85岁}
Point{x=41.7508万, y=84岁}
Point{x=56.8514万, y=83岁}
Point{x=72.7173万, y=77岁}
Point{x=96.9255万, y=45岁}

效果当然是一样的

posted @ 2021-12-21 18:29  jentreywang  阅读(69)  评论(0编辑  收藏  举报