一、排序原理简介
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 的好处是不需要修改对象的源代码, 而是实现一个自定义的外置比较器, 当某个自定义的对象集合需要排序时,把比较器和对象集合一起传递就可以比大小了,两个比较器使用规则如下:
- 如果实现类没有实现Comparable接口,又想对两个类进行比较大小,可以实现Comparator接口自定义一个比较器,完成比较大小的逻辑;
- 实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较大小的逻辑要修改Comparable接口的实现类;而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。
原文地址: https://blog.csdn.net/u011635492/article/details/123607347
本文介绍了Java中排序原理,包括Arrays.sort()和Collections.sort()的实现,并详细讲解了Comparable和Comparator接口。Comparable是内比较器,用于对象的自然排序;Comparator是外比较器,适用于自定义排序规则。文中给出了多个示例来演示如何使用这两个接口进行排序。
4万+

被折叠的 条评论
为什么被折叠?



