Comparable和Comparator

本文介绍了Java中排序原理,包括Arrays.sort()和Collections.sort()的实现,并详细讲解了Comparable和Comparator接口。Comparable是内比较器,用于对象的自然排序;Comparator是外比较器,适用于自定义排序规则。文中给出了多个示例来演示如何使用这两个接口进行排序。

一、排序原理简介

1.数组Arrays.sort()排序原理

  通过Java API文档知道,Arrays.sort()调用的是DualPivotQuicksort.sort()方法,如下代码片段所示:

public static void sort(int[] a) {
    DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}

  DualPivotQuicksort.sort()排序算法是双枢轴快速排序,时间复杂度为O(n log(n)),具体排序代码参见JDK API文档DualPivotQuicksort类中的sort()方法。Arrays.sort()只能用于基本数据类型:int、short、byte、long、float、double、char的排序。

2.排序方法Arrays.sort(Object[])

  根据元素的自然顺序对指定对象数组按升序进行排序。数组中的所有元素都必须实现 Comparable 接口。此外,数组中的所有元素都必须是可相互比较的(也就是说,对于数组中的任何 e1 和 e2 元素而言,e1.compareTo(e2) 不得抛出 ClassCastException)。该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)该排序算法的时间复杂度为O(n log(n)),且是稳定的排序算法:不会因调用 sort 方法而对相等的元素进行重新排序, 如果数组包含不可相互比较的 的元素(例如,字符串和整数)抛出ClassCastException,此方法主要用于集合排序Collections.sort(),其调用List接口中的sort()方法进行排序。

3.集合Collections.sort()排序原理

  通过Java API文档知道,Collections.sort()有两个实现方法,如下代码片段所示:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

  代码中list.sort()方法调用的就是Arrays.sort(Object [])方法,如下代码片段所示:

default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}

二、Comparable和Comparator接口

1. Comparable接口简介

  Comparable 是内比较器接口,在比较排序时是以自身作为基对象与传入的对象进行大小比较。如果一个类实现了Comparable接口,那么说明该类支持按照指定规则(compareTo方法)进行大小比较:如果A.compareTo(B)返回小于零的数(负数),则说明A小于B;如果A.compareTo(B)返回等于零的数,则说明A与B相等;如果A.compareTo(B)返回大于零的数(正数),则说明A大于B。

2. Comparator接口简介

  Comparator 是外比较器接口。若我们需要对某个对象集合进行排序,而该对象本身不支持排序(即没有实现Comparable接口);那我们可以创建一个该对象的外比较器来进行排序。这个比较器只需要实现Comparator接口,重写Comparator接口的compare()方法(即指定对象的大小比较规则)。然后通过该比较器对类进行排序。int compare(T o1, T o2)和上面的A.compareTo(B)类似,如果int compare(T o1, T o2)返回小于零的数,则说明o1小于o2;如果int compare(T o1, T o2)返回等于零,则说明o1等于o2;如果int compare(T o1, T o2)返回大于零的数,则说明o1大于o2。

3.Comparable和Comparator本质

  Comparable和Comparator接口本身并不具备排序功能,它只是进行了两个对象按照指定规则的大小比较,Collections.sort()和List.sort()均调用了Arrays.sort(Object[])方法对实现了Comparable接口的对象集合进行排序,如果对象实现的比较规则compareTo()不满足要求,或者需要传入自定义的比较规则时,就可以将外比较器Comparator作为参数传入排序方法中,Collections.sort()和List.sort()均支持可选择的外部排序参数。

4.Comparable排序示例

  创建POJO对象Student类:

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private int score;
    public Student() {}
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student:{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}';
    }
    /**
     * 基于学生student对象的age排序
     * @param student
     * @return
     */
    @Override
    public int compareTo(Student student) {
        return this.age - student.age;
    }
}

  基于对象数组排序:

public class Main {
    public static void main(String[] args) {
        Student student1 = new Student("c", 18, 80);
        Student student2 = new Student("a", 20, 75);
        Student student3 = new Student("e", 17, 86);
        Student student4 = new Student("d", 16, 72);
        Student[] student = {student1, student2, student3, student4};
        System.out.println("对象数组排序前:");
        for( Student stu: student){
            System.out.println(stu.toString());
        }
        Arrays.sort(student);
        System.out.println("对象数组排序后:");
        for(Student stu: student){
            System.out.println(stu.toString());
        }
    }
}

  基于数组排序输出结果:

对象数组排序前:
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}
Student:{name='e', age=17, score=86}
Student:{name='d', age=16, score=72}
对象数组排序后:
Student:{name='d', age=16, score=72}
Student:{name='e', age=17, score=86}
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}

  基于对象集合排序:

public class ObjectCompare {
    public static void main(String[] args) {
        List<Student> studentList = new ArrayList<>();
        Student student1 = new Student("c", 18, 80);
        Student student2 = new Student("a", 20, 75);
        Student student3 = new Student("e", 17, 86);
        Student student4 = new Student("d", 16, 72);
        studentList.add(student1);
        studentList.add(student2);
        studentList.add(student3);
        studentList.add(student4);
        System.out.println("按照对象集合排序前:");
        for(Student student: studentList){
            System.out.println(student.toString());
        }
        Collections.sort(studentList);
        System.out.println("按照对象集合排序后:");
        for(Student stuList: studentList){
            System.out.println(stuList.toString());
        }
    }
}

  基于对象集合排序输出结果:

对象集合排序前:
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}
Student:{name='e', age=17, score=86}
Student:{name='d', age=16, score=72}
对象集合排序后:
Student:{name='d', age=16, score=72}
Student:{name='e', age=17, score=86}
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}

  基于对象字符串属性排序:需要重写比较器Comparable接口的compareTo()方法,如下代码片段所示:

/**
 * 基于学生student对象的name排序
 * @param student
 * @return
 */
@Override
public int compareTo(Student student) {
    return this.name.compareTo(student.name);
}

  基于数组对象和集合排序的结果相同,如下图所示:

按照对象集合排序前:
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}
Student:{name='e', age=17, score=86}
Student:{name='d', age=16, score=72}
按照对象集合排序后:
Student:{name='a', age=20, score=75}
Student:{name='c', age=18, score=80}
Student:{name='d', age=16, score=72}
Student:{name='e', age=17, score=86}
5.Comparator排序示例

  未实现Comparable接口的Student类,如下代码所示:

public class Student{
    private String name;
    private int age;
    private int score;
    public Student() {}
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student:{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}';
    }
}

  使用匿名内部类基于age属性排序:

public class Main {
    public static void main(String[] args) {
        Student student1 = new Student("c", 18, 80);
        Student student2 = new Student("a", 20, 75);
        Student student3 = new Student("e", 17, 86);
        Student student4 = new Student("d", 16, 72);
        Student[] student = {student1, student2, student3, student4};
        System.out.println("对象数组排序前:");
        for( Student stu: student){
            System.out.println(stu.toString());
        }
        Arrays.sort(student, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println("对象数组排序后:");
        for(Student stu: student){
            System.out.println(stu.toString());
        }
    }
}

  基于对象数组排序结果:

对象数组排序前:
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}
Student:{name='e', age=17, score=86}
Student:{name='d', age=16, score=72}
对象数组排序后:
Student:{name='d', age=16, score=72}
Student:{name='e', age=17, score=86}
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}

  使用匿名内部类基于name属性排序:

public class ObjectCompare {
    public static void main(String[] args) {
        // 初始化对象数组并按照年龄输出
        List<Student> studentList = new ArrayList<>();
        Student student1 = new Student("c", 18, 80);
        Student student2 = new Student("a", 20, 75);
        Student student3 = new Student("e", 17, 86);
        Student student4 = new Student("d", 16, 72);
        studentList.add(student1);
        studentList.add(student2);
        studentList.add(student3);
        studentList.add(student4);
        System.out.println("按照对象集合排序前:");
        for(Student student: studentList){
            System.out.println(student.toString());
        }
        Collections.sort(studentList, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        System.out.println("按照对象集合排序后:");
        for(Student stuList: studentList){
            System.out.println(stuList.toString());
        }
    }
}

  基于对象集合排序结果:

按照对象集合排序前:
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}
Student:{name='e', age=17, score=86}
Student:{name='d', age=16, score=72}
按照对象集合排序后:
Student:{name='a', age=20, score=75}
Student:{name='c', age=18, score=80}
Student:{name='d', age=16, score=72}
Student:{name='e', age=17, score=86}

  自定义比较器对象排序,代码片段如下:

public class ObjectCompare {
    public static void main(String[] args) {
        // 初始化对象数组并按照年龄输出
        List<Student> studentList = new ArrayList<>();
        Student student1 = new Student("c", 18, 80);
        Student student2 = new Student("a", 20, 75);
        Student student3 = new Student("e", 17, 86);
        Student student4 = new Student("d", 16, 72);
        studentList.add(student1);
        studentList.add(student2);
        studentList.add(student3);
        studentList.add(student4);
        System.out.println("按照对象集合排序前:");
        for(Student student: studentList){
            System.out.println(student.toString());
        }
        Collections.sort(studentList, new StudentComparator());
        System.out.println("按照对象集合排序后:");
        for(Student stuList: studentList){
            System.out.println(stuList.toString());
        }
    }
}

class StudentComparator implements Comparator<Student>{
    @Override
    public int compare(Student stu1, Student stu2) {
        return stu1.getName().compareTo(stu2.getName());
    }
}

  基于对象集合排序结果:

按照对象集合排序前:
Student:{name='c', age=18, score=80}
Student:{name='a', age=20, score=75}
Student:{name='e', age=17, score=86}
Student:{name='d', age=16, score=72}
按照对象集合排序后:
Student:{name='a', age=20, score=75}
Student:{name='c', age=18, score=80}
Student:{name='d', age=16, score=72}
Student:{name='e', age=17, score=86}

三、总结

  两种比较器接口各有优劣,用Comparable只要实现Comparable接口的compareTo()方法,对象就成为一个可以比较大小的对象,但是需要修改对象的源代码;用Comparator 的好处是不需要修改对象的源代码, 而是实现一个自定义的外置比较器, 当某个自定义的对象集合需要排序时,把比较器和对象集合一起传递就可以比大小了,两个比较器使用规则如下:

  1. 如果实现类没有实现Comparable接口,又想对两个类进行比较大小,可以实现Comparator接口自定义一个比较器,完成比较大小的逻辑;
  2. 实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较大小的逻辑要修改Comparable接口的实现类;而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。

原文地址: https://blog.csdn.net/u011635492/article/details/123607347

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抽离的心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值