排序的比较方式

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

一、自然排序

  Comparable 接口中只提供了一个方法: compareTo(Object obj) ,该方法的返回值是 int 。如果返回值为正数,则表示当前对象(调用该方法的对象)比 obj 对象“大”;反之“小”;如果为零的话,则表示两对象相等。下面是一个实现了 Comparable 接口的 Student 类: 

public class Student implements Comparable {

    private int id;
    private String name;

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

    public Student() {
    }

    @Override
    public int compareTo(Object o) {
        if (o instanceof Student) {
            Student student = (Student) o;
            return id - student.id;
        }
        return 0;
    }

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

  Student 实现了自然排序接口 Comparable ,那么我们是怎么利用这个接口对一组 Student 对象进行排序的呢?我们在学习数组的时候,使用了一个类来给整型数组排序: java.util.Arrays 。我们使用 Arrays 的 sort 方法来给整型数组排序。翻翻 API 文档就会发现, Arrays 里给出了 sort 方法很多重载形式,其中就包括 sort(Object[] obj) ,也就是说 Arryas 也能对对象数组进行排序,排序过程中比较两个对象“大小”时使用的就是 Comparable 接口的 compareTo 方法。 

public class CompareTest {
    public static void main(String[] args) {
        Student stu1 = new Student(1, "Little");
        Student stu2 = new Student(2, "Cyntin");
        Student stu3 = new Student(3, "Tony");
        Student stu4 = new Student(4, "Gemini");

        Student[] stus = new Student[4];
        stus[0] = stu1;
        stus[1] = stu4;
        stus[2] = stu3;
        stus[3] = stu2;
        System.out.println("Array:" + Arrays.toString(stus));
        Arrays.sort(stus);
        System.out.println("Sort:" + Arrays.toString(stus));
    }
}

  Student 数组里添加元素的顺序并不是按学号 id 来添加的。调用了 Arrays.sort(stus) 之后,对 Student 数组进行排序,不管 sort 是使用哪种排序算法来实现的,比较两个对象“大小”这个操作,它是肯定要做的。那么如何比较两个对象的“大小”? Student 实现的 Comparable 接口就发挥作用了。 sort 方法会将待比较的那个对象强制类型转换成 Comparable ,并调用 compareTo 方法,根据其返回值来判断这两个对象的“大小”。所以,在这个例子中排序后的原 Student 乱序数组就变成了按学号排序的 Student 数组。 

  但是我们注意到,排序算法和 Student 类绑定了, Student 只有一种排序算法。但现实社会不是这样的,如果我们不想按学号排序怎么办?假如,我们想按姓名来给学生排序怎么办?我们只能修改 Student 类的 Comparable 接口的 compareTo 方法,改成按姓名排序。如果在同一个系统里有两个操作,一个是按学号排序,另外一个是按姓名排序,这怎么办?不可能在 Student 类体中写两个 compareTo 方法的实现。这么看来Comparable就有局限性了。为了弥补这个不足,JDK 还为我们提供了另外一个排序方式,也就是下面要说的比较器排序。 

二、比较器排序

  上面我提到了,之所以提供比较器排序接口,是因为有时需要对同一对象进行多种不同方式的排序,这点自然排序 Comparable 不能实现。另外, Comparator 接口的一个好处是将比较排序算法和具体的实体类分离,降低类之间的耦合

  翻翻 API 会发现, Arrays.sort 还有种重载形式:sort(T[] a, Comparator<? super T> c) ,这个方法参数的写法用到了泛型,这个方法的意思是按照比较器 c 给出的比较排序算法,对 Object 数组进行排序。Comparator 接口中定义了两个方法: compare(Object o1, Object o2) 和 equals 方法,由于 equals 方法所有对象都有的方法,因此当我们实现 Comparator 接口时,我们只需重写 compare 方法,而不需重写 equals 方法。Comparator 接口中对重写 equals 方法的描述是:“注意,不重写 Object.equals(Object) 方法总是安全的。然而,在某些情况下,重写此方法可以允许程序确定两个不同的 Comparator 是否强行实施了相同的排序,从而提高性能。”。我们只需知道第一句话就OK了,也就是说,可以不用去想应该怎么实现 equals 方法,因为即使我们不显示实现 equals 方法,而是使用Object类的 equals 方法,代码依然是安全的。而对于第二句话,究竟是怎么提高比较器性能的,我也不了解,所以就不说了。 
  那么我们来写个代码,来用一用比较器排序。还是用 Student 类来做,只是没有实现 Comparable 接口。 

public class Comparator {
    public static class Student{
        public String name;
        public int id;
        public int age;

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

    public static class IdAscendingComparator implements java.util.Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o1.id - o2.id;
        }
    }

    public static class IdDescendingComparator implements java.util.Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o2.id - o1.id;
        }
    }

    public static class AgeAscendingComparator implements java.util.Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o1.age - o2.age;
        }

    }

    public static class AgeDescendingComparator implements java.util.Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o2.age - o1.age;
        }

    }

    public static void printStudents(Student[] students) {
        for (Student student : students) {
            System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
        }
        System.out.println("===========================");
    }

    public static void main(String[] args) {
        Student student1 = new Student("A", 1, 23);
        Student student2 = new Student("B", 2, 21);
        Student student3 = new Student("C", 3, 22);

        Student[] students = new Student[] { student3, student2, student1 };
        //没指定比较器时,比较的是对象的地址,无意义
        printStudents(students);

        Arrays.sort(students, new IdAscendingComparator());
        printStudents(students);

        Arrays.sort(students, new IdDescendingComparator());
        printStudents(students);

        Arrays.sort(students, new AgeAscendingComparator());
        printStudents(students);

        Arrays.sort(students, new AgeDescendingComparator());
        printStudents(students);

    }
}

 

 

参考:https://crazylittle-163-com.iteye.com/blog/513900

posted @ 2019-05-25 14:46  yi0123  阅读(369)  评论(0编辑  收藏  举报