Java——Comparable接口和Comparator接口的区别

Java——Comparable接口和Comparator接口的区别

Comparable接口

Comparable是一个排序接口。若一个类实现了Comparable接口,即代表该类实现了compareTo方法,该方法规定了该类的对象的比较规则(两个对象如何比较“大小”)。
类通过实现o1.compareTo(o2)方法来比较o1o2的大小:

  • 若返回正数,意味着o1大于o2
  • 若返回负数,意味着o1小于o2
  • 若返回零,则意味着o1等于o2

Comparator接口

Comparator是比较器接口。若我们只需要控制某些对象的次序,而类本身并不支持排序(即没有实现Comparable接口),对于这种情况,我们可以通过建立一个比较器(即关于该类的一个Comparator接口实现类)来进行排序。
比较器需实现方法compare(T o1, T o2),其原理与上面的o1.compareTo(o2)方法类似,这里就不再描述。

两者的联系

Comparable接口实现在自身类中,代表默认排序,相当于内部比较器;而Comparator接口则是为类外实现的一个比较器,相当于外部比较器

案例加深理解

用于比较的自定义Student类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Student implements Comparable<Student> {
private String name;
private int grade;

public Student() {}

public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}

public void setName(String name) {
this.name = name;
}

public void setGrade(int grade) {
this.grade = grade;
}

public String getName() {
return name;
}

public int getGrade() {
return grade;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", grade=" + grade +
'}';
}

@Override
@Override
public int compareTo(Student o) {
return this.getGrade() > o.getGrade() ? 1 : this.getGrade() < o.getGrade() ? -1 : this.getName().compareTo(o.getName());
}
}

上述Student类实现了Comparable接口,带有默认排序,调试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
List<Student> arr = new ArrayList<>();
arr.add(new Student("John", 100));
arr.add(new Student("Bob", 75));
arr.add(new Student("Alice", 100));
arr.add(new Student("Jake", 90));
System.out.println(arr);
System.out.println("------------------------");
Collections.sort(arr); //默认排序
System.out.println(arr);

}
}

另外定义一个实现Compartor接口的比较器如下:

1
2
3
4
5
6
public class StuCompartor implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getGrade() > o2.getGrade() ? 1 : o1.getGrade() < o2.getGrade() ? -1 : o1.getName().compareTo(o2.getName());
}
}

调试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StuMain {
public static void main(String[] args) {
List<Student> arr = new ArrayList<>();
arr.add(new Student("John", 100));
arr.add(new Student("Bob", 75));
arr.add(new Student("Alice", 100));
arr.add(new Student("Jake", 90));
System.out.println(arr);
System.out.println("------------------------");
Collections.sort(arr, new StuCompartor());//外部比较器
System.out.println(arr);
}
}

运行结果均如下所示:

1
2
3
[Student{name='John', grade=100}, Student{name='Bob', grade=75}, Student{name='Alice', grade=100}, Student{name='Jake', grade=90}]
------------------------
[Student{name='Bob', grade=75}, Student{name='Jake', grade=90}, Student{name='Alice', grade=100}, Student{name='John', grade=100}]

扩展

若只排序一次,则可不额外定义比较器,直接利用匿名内部类即可:

1
2
3
4
5
6
Collections.sort(arr, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getGrade() > o2.getGrade() ? 1 : o1.getGrade() < o2.getGrade() ? -1 : o1.getName().compareTo(o2.getName());
}
});

很明显上述写法仍然让人觉得繁琐、复杂,进一步可利用Lambda表达式进行化简:

1
2
3
Collections.sort(arr, (Student o1, Student o2) -> {
return o1.getGrade() > o2.getGrade() ? 1 : o1.getGrade() < o2.getGrade() ? -1 : o1.getName().compareTo(o2.getName());
});

再进行一些化简(去掉参数类型):

1
2
3
Collections.sort(arr, (o1, o2) -> {
return o1.getGrade() > o2.getGrade() ? 1 : o1.getGrade() < o2.getGrade() ? -1 : o1.getName().compareTo(o2.getName());
});

最终利用方法应用进行化简(同时定义了首选比较量相等时的继续比较依据):

1
2
Collections.sort(arr, comparing(Student::getGrade)
.thenComparing(Student::getName));

若需要逆序:

1
2
3
Collections.sort(arr, comparing(Student::getGrade)
.reversed()
.thenComparing(Student::getName));