Java提供了 Comparable
和 Comparator
两个接口,为我们提供了为对象自定义排序逻辑的能力。本文中,我们将探讨 Comparable
和 Comparator
之间的差异,何时如何使用它们。
Comparable 接口:
如下是源码中 Comparable
接口的定义:
package java.lang;
/**
* 该接口对实现它的每个类的对象强加了总排序。
* 这种排序称为类的自然排序,而类的compareTo 方法称为其自然比较方法。
*/
public interface Comparable<T> {
int compareTo(T o);
}
Comparable 接口用于定义对象的自然顺序。若一个类实现了 Comparable
接口,则表明其实例可以相互比较,可以理解为 该类支持排序。
Comparable
接口由一个名为 的方法组成 compareTo()
,该方法返回一个表示比较结果的整数值。
compareTo()
方法 返回值/比较结果 的的规则:
- 如果调用对象小于正在比较的对象,则返回负整数。
- 如果调用对象等于正在比较的对象,则为零。
- 如果调用对象大于正在比较的对象,则为正整数。
一个类实现了 Comparable
接口,它就支持自然排序了。
- 可以使用
Arrays.sort()
或 Collections.sort()
方法对保存该对象的集合进行排序。
- 可以不指定比较器直接保存到有序集合(如:
TreeSet
, TreeMap
)中。
注意:
- 强烈建议(尽管不强制要求)自然排序与
equals
保持一致。
null
不是任何类的实例,即使 e.equals(null)
返回 false,e.compareTo(null)
也应该抛出 NullPointerException
。
下面的例子定义 Person 类并实现 Comparable
接口:
package cn.itmob.demo;
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public int compareTo(Person o) {
return this.age - o.getAge();
}
}
创建保存 Person 实例的集合并排序:
package cn.itmob.demo;
public class SortingExample {
public static void main(String[] args) {
Person[] people = {
new Person("zhang san", 30),
new Person("li si", 20),
new Person("wang wu", 40)
};
Arrays.sort(people);
for (Person person : people) {
System.out.println(person.getName() + ": " + person.getAge());
}
}
}
输出排序结果:
li si: 20
zhang san: 30
wang wu: 40
Comparator 接口
如下是源码中 Comparator
接口的定义:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
// 核心的方法有两个:compare() 和 equals()。
// 从 JDK 1.8 开始新增了多个其他方法对函数式编程进行支持,详见其他文章,这里不过多介绍。
Comparator
接口是比较器接口,提供了一种方法来对未实现 Comparable
接口的对象进行排序,或者为已经实现了 Comparable
接口的对象定义自定义的排序逻辑。Comparator 接口允许您创建单独的比较逻辑,而无需修改原始类。
Comparator
接口定义了一个名为 compare()
的方法,该方法将两个对象作为输入参数并返回一个表示比较结果的整数值。
compare()
方法的返回值/比较结果 的规则与 compareTo()
方法相同:
- 如果第一个对象小于第二个对象,则返回负整数。
- 如果第一个对象等于第二个对象,则返回零。
- 如果第一个对象大于第二个对象,则返回正整数。
下面是 Comparator
接口的实现,根据年龄比较两个 Person
实例:
package cn.itmob.demo;
import java.util.Comparator;
public class NameComparator implements Comparator<Person> {
@Override
public int compare(Person person1, Person person2) {
return person1.getName().compareTo(person2.getName());
}
}
package cn.itmob.demo;
public class SortingExample {
public static void main(String[] args) {
Person zhangSan = new Person("zhang san", 30);
Person liSi = new Person("li si", 20);
Person liSi2 = new Person("li si", 40);
NameComparator comparator = new NameComparator();
System.out.println(comparator.compare(zhangSan, liSi));
System.out.println(comparator.compare(liSi, zhangSan));
System.out.println(comparator.compare(liSi, liSi2));
}
}
比较结果:
14
-14
0
如何在排序中使用自定义比较器:
package cn.itmob.demo;
public class SortingExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("zhang san", 25),
new Person("li si", 42),
new Person("wang wu", 30));
Collections.sort(people, new NameComparator());
for (Person person : people) {
System.out.println(person.getName() + ": " + person.getAge());
}
}
}
使用自定义比较器根据名称进行排序的结果:
li si: 42
wang wu: 30
zhang san: 25
如果在排序方法中没有提供比较器,也没有实现 Comparable
接口并重写 compareTo
方法,执行时会出现没有找到任何排序的方法的错误:
SortingExample.java:38: error: no suitable method found for sort(List<Person>)
Collections.sort(people);
^
method Collections.<T#1>sort(List<T#1>) is not applicable
(inference variable T#1 has incompatible bounds
equality constraints: Person
upper bounds: Comparable<? super T#1>)
method Collections.<T#2>sort(List<T#2>,Comparator<? super T#2>) is not applicable
(cannot infer type-variable(s) T#2
(actual and formal argument lists differ in length))
where T#1,T#2 are type-variables:
T#1 extends Comparable<? super T#1> declared in method <T#1>sort(List<T#1>)
T#2 extends Object declared in method <T#2>sort(List<T#2>,Comparator<? super T#2>)
Comparable 和 Comparator 之间的区别:
- 一个类实现了
Comparable
接口,意味着该它可以直接进行比较/排序,但比较的方式只有这一种。
- 一个类无论是否实现了
Comparable
接口,如果需要进行不同方式的比较/排序,则可以自定义一个或多个比较器(实现 Comparator
接口)。
Comparable 和 Comparator 如何选择呢?
使用 Comparable
还是 Comparator
要根据应用程序实际的要求来选择。
以下是一些规则:
- 如果排序逻辑是要排序的对象固有的并且不会更改,使用 Comparable。
- 如果需要定义多个排序规则或对未实现 Comparable 的对象进行排序时,使用 Comparator。
- 如果一个类实现了 Comparable 接口支持自然排序,仍然可以使用 Comparator 进行自定义排序。
总结:
如果需要基于自然顺序来排序,选择 Comparable
,如果要按照对象的不同属性进行排序或需要多种排序逻辑,选择 Comparator
。