文章目录
- Arrays和Collections工具类
- > Arrays.sort
- > Collections.sort
- Comparable与Comparator的区别
- 源码中对Comparable接口的实现
- 使用Comparable接口完成第一个对象排序实例
- 使用Comparator比较器完成对象排序
- Collecitons.sort的底层实现
- 1. 使用Comparator的实现
- 2. 实现Comparable接口的实现
Arrays和Collections工具类
Java为我们提供了实用的操作数组和集合的工具类,Arrays和Collections。内含对数组或集合的各种排序方法,数组与集合的转换方法。
> Arrays.sort
Arrays.sort()有两个重要的重载方法,一个是直接传入要排序的数组,另一个是传入数组和comparator比较器。
在进行对象排序时,默认调用TimSort,TimSort是归并排序的优化版本一种比MergeSort更高效的排序方式。
> Collections.sort
Collections.sort()与Arrays.sort()传参基本相同。
collections中的数据在排序前需要输入到array中,接着调用Arrays.sort函数来完成对象排序。
关于详细的排序实现,以后单独写文章细讲。
Comparable与Comparator的区别
Comparable和Comparator都是用来实现集合中元素的比较、排序的。
Comparable是在集合内部定义的方法实现的排序,位于java.util下。Comparator是在集合外部实现的排序,位于java.lang下。
Comparable是一个对象本身就已经支持自比较所需要实现的接口,如String、Integer自己就实现了Comparable接口,可完成比较大小操作。自定义类要在加入list容器中后能够排序,也可以实现Comparable接口,在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。
Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。Comparator体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
总而言之Comparable是自已完成比较,Comparator是外部程序实现比较。
引用:
源码中对Comparable接口的实现
先看源码中对Comparable接口作出的定义:
该接口对每个类的对象施加一个总的排序实现它,这种顺序被称为类的自然排序,类的比较方法(CompareTo)称为其自然比较方法。
实现此接口的List集合(或数组)可以由Collections.sort和Arrays.sort自动排序。实现此接口的对象可以作为SortedMap中的键或SortedSet中的元素使用,而不需要指定Comparator。
官方解释的比较清楚。对于不清楚如何排序的对象,需要该对象的类使用Comparable接口来指定排序的方法(如果该类不实现Comparable接口,也可以指定一个Comparator进行排序,这点一会说)。 随后,就可以使用Arrays.sort或Collections.sort进行排序了。
看一个源码对其实现的简单的例子:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] arr = {"E", "A", "D", "B", "C"};
Arrays.sort(arr);
for (String str : arr) {
System.out.println(str);
}
}
}
输出结果:A B C D E
String类是对象,在这里我们没有对String类做什么操作,只是赋值,为什么String类能实现自动排序?我们深入到底层源码看一下
实现的CompareTo方法:
可以看到,之所以String自动完成了排序,是String类在设计的时候实现了Comparable接口,重写了compareTo方法。这样,在调用Arrays.sort时,对象会参考重写的compareTo方法进行排序。
使用Comparable接口完成第一个对象排序实例
String类的Comparable接口使用了泛型,通过查看Comparable接口的源码可以看到,这里的泛型规定了Object类的具体子类,一般传入实现该接口的类即可,比如Student类,泛型传入Student。
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
参考String类的设计就可以使用Comparable接口自己实现对象排序了。
值得注意的是,若Comparable接口不使用泛型,在重写compareTo方法时需要向下转型。
Student类:
public class Student implements Comparable<Student> {
String name;
int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
if(this.age == o.age) {
return this.name.compareTo(o.name);
} else {
return this.age - o.age;
}
}
}
主类:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Tina", 20));
students.add(new Student("Cindy", 21));
students.add(new Student("Cathy", 21));
students.add(new Student("Helen", 24));
students.add(new Student("Alen", 20));
Collections.sort(students);
for (Student student : students) {
System.out.println(student);
}
}
}
以Student对象的年龄升序排列,输出结果:
Student{name=‘Alen’, age=20}
Student{name=‘Tina’, age=20}
Student{name=‘Cathy’, age=21}
Student{name=‘Cindy’, age=21}
Student{name=‘Helen’, age=24}
在主类中,先加入Cindy后加入Cathy,排序后变成了先Cathy后Cindy,这是因为在重写compareTo方法时定义了年龄相同时按名字升序排列。
使用Comparator比较器完成对象排序
Comparator其实就是把比较方法单独拿了出来,使用时单独传入sort方法里。改用Comparator只需改动部分代码:
Student类:
public class Student {
String name;
int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
主类:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Tina", 20));
students.add(new Student("Cindy", 21));
students.add(new Student("Cathy", 21));
students.add(new Student("Helen", 24));
students.add(new Student("Alen", 20));
Collections.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if(o1.age == o2.age) {
return o1.name.compareTo(o2.name);
} else {
return o1.age - o2.age;
}
}
});
for (Student student : students) {
System.out.println(student);
}
}
}
Student类正常写,主类中可以看到sort方法后传入了两个参数,第一个是要排序的集合,第二个就是comparator比较器了,这里直接new Comparator接口后加花括号的方法是匿名接口实现,ta等同于如下:
新建StudentComparator类实现Comparator接口:
import java.util.Comparator;
public class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
if(o1.age == o2.age) {
return o1.name.compareTo(o2.name);
} else {
return o1.age - o2.age;
}
}
}
Collections.sort改为:
StudentComparator comparator = new StudentComparator();
Collections.sort(students, comparator);
输出结果与上面一致
Collecitons.sort的底层实现
1. 使用Comparator的实现
首先开启debug调试,进入Collecitons.sort方法
Collections.sort调用的是集合对象内置的sort方法,单步进入list.sort
这里的expectedModCount是出于线程安全考虑而增设的变量,我们把重点放在Arrays.sort方法这一句上(可以看到Colletions.sort本质上还是Arrays.sort),继续单步进入
rangeSort是检查数组范围是否合法,不合法会抛出异常
LegacyMergeSort,追踪其位置,可以看到官方解释
Old merge sort implementation can be selected (for
compatibility with broken comparators) using a system property.
Cannot be a static boolean in the enclosing class due to
circular dependencies. To be removed in a future release.
意思就是这是一种老的归并排序,现在暂时不会用了,未来会移除,所以返回值为flase。TimSort是对归并排序做了大量优化的新版本,所以程序会跳转到TimSort.sort方法中,完成对象排序。
2. 实现Comparable接口的实现
唯一一点与Comparator不一致的地方是c为null,因为我们没有显式传入Comparator对象。
进入sort方法后,其内部调用的是ComparableTimSort,与TimSort没有区别。