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;
}
@Override
public 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()
}
@Override
public 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>() {
@Override
public 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;
}
@Override
public 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对比,则为升序排列;反之为降序排列