Java 比较器 Comparable 和 Comparator
在 Java 中对数组或者集合排序,Java提供了 `Arrays.sort()` 和 `Collections.sort()` 两种排序方法。前者会对数组进行排序,后者会对集合进行排序。至于他们排序的规则,我们可以通过下面的两个接口来定义。
- 对象实现 Comparable 接口,这是一个内部排序接口
- 排序时实现 Comparator,作为参数传入到
Arrays.sort()和Collections.sort(),这是一个外部排序接口
Comparable
这是一个内部排序接口,如果一个类实现了这个接口,就说明这个类是支持排序的。那么我们就可以直接通过 `Arrays.sort()` 和 `Collections.sort()` 对该类组成的数组或者集合直接进行排序。在 `Comparable` 的官方文档中称这种排序方法为自然排序。
Comparable 接口只有一个方法 compareTo(T o) ,实现的该接口的所有类必须实现这个方法, compareTo(T o) 会返回一个 int 值,这个 int 值可能是 负数,正数和零。它的实现类会跟 compareTo(T o) 传入的对象进行比较。负数表示该对象比传入的参数小;正数表示该对象比传入的对象大;零表示该对象跟传入的对象一样大。
当然我们不一定通过数值的大小来进行排序,有可能通过其它的属性来排序,我们依然可以通过 `compareTo(T o)` 的返回值来进行排序。如下:
public class ComparableSample {static List<Person> list = new ArrayList<>();public static void main(String[] args) {list.add(new Person("Ricky", 21));list.add(new Person("Jack", 20));list.add(new Person("James", 24));list.add(new Person("Tracy", 23));list.add(new Person("Jessy", 26));list.add(new Person("Steven", 25));Collections.sort(list);list.forEach(person -> System.out.println(person.toString()));}static class Person implements Comparable<Person> {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Person o) {if (this.name.length() > o.name.length()) {return 1;} else if (this.name.length() == o.name.length()) {return 0;} else {return -1;}// return this.name.length() - o.name.length()}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +"length='" + name.length() + "\'" +", age=" + age +'}';}}}
如上代码,我们定义了一个 Person 类,实现了 Comparable 接口,并实现了它的 compareTo 方法。这里我们以 name的长度为标准进行排序。如果 当前的 Person 对象的name的长度大于 compareTo 传入的 Person 对象的 name的长度,就返回1,该值为正数;如果相等就返回0;如果小于就返回-1,为负数。而由于 Collections.sort(List<T> list)是对集合进行升序排列,所以运行结果如下,根据name的长度进行升序排列:
Person{name='Jack'length='4', age=20}Person{name='Ricky'length='5', age=21}Person{name='James'length='5', age=24}Person{name='Tracy'length='5', age=23}Person{name='Jessy'length='5', age=26}Person{name='Steven'length='6', age=25}
上面的例子是对于当前对象与传入对象的之间比较,当我们把两者对调一下,就会变成降序排列了。
如果当前的对象去与传入对象进行比较,则为升序;如果传入对象与当前的对象比较,则为降序。
Comparator
使用 `Comparator` 接口,不需要集合或者数组的每个元素都去实现 `Comparator` ,只需在调用`Arrays.sort()` 和 `Collections.sort()` 时传入我们自定义的对比规则。
public class ComparableSample {static List<Person> list = new ArrayList<>();public static void main(String[] args) {list.add(new Person("Ricky", 21));list.add(new Person("Jack", 20));list.add(new Person("James", 24));list.add(new Person("Tracy", 23));list.add(new Person("Jessy", 26));list.add(new Person("Steven", 25));Collections.sort(list, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.name.length() - o2.name.length();}});list.forEach(person -> System.out.println(person.toString()));}static class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +"length='" + name.length() + "\'" +", age=" + age +'}';}}}
上面的代码中,Person 并没有实现任何接口,只是在调用排序方法时,传入的一个排序的规则。它的规则跟 Comparable 很像。如果集合中第一个元素与第二个比较,如果返回 0,表示两者相等;如果返回正数,表示第一个大于第二个;如果为负数表示第一个小于第二个。所以上面的代码运行结果依然是根据name 进行升序排列。
Person{name='Jack'length='4', age=20}Person{name='Ricky'length='5', age=21}Person{name='James'length='5', age=24}Person{name='Tracy'length='5', age=23}Person{name='Jessy'length='5', age=26}Person{name='Steven'length='6', age=25}
总结
- Comparable 和 Comparator 都可以对数组或者集合进行排序
- Comparable 针对的是数组/集合的每一个元素的内部排序
- Comparator 则是在数组/集合的元素的外部定义排序规则
- Comparable 和 Comparator 排序规则很相似。默认都是升序排列。
- Comparable中如果当前对象与
compareTo(T o)传入对象对比,则为升序排列;反之为降序排列 - Comparator中如果
compare(T o1, T o2)中的 o1 与 o2对比,则为升序排列;反之为降序排列
