Java排序之Comparable与Comparator

平时我们在对集合或数组排序时,会碰到Comparable和Comparator这两个很类似的接口,它们以不同的方式实现了元素的排序功能,今天我们就来谈谈这两个接口的区别,并介绍下Java排序一些常见方法的使用。

首先介绍一下Comparable与Comparator的区别。

Comparable是排序接口,若一个类实现了Comparable接口,该类的对象就支持排序;

而Comparator是比较器接口,我们可以实现一个Comparator接口(一般采用匿名内部类的写法),定义一种对A类的排序规则,而不需要对A类做任何改变。

所以,Comparable相当于内部比较器,而Comparator相当于外部比较器。

下面列出集合排序时一些常用的方法:

  • list.sort(comparator)
  • Collections.sort(list)
  • Collections.sort(list, comparator)
  • list.stream().sorted();
  • list.stream().sorted(comparator)

可以发现有的排序方法要求提供Comparator接口作为参数,有些则没有要求;

若提供了Comparator接口,则集合的元素无需实现Comparable接口,若元素没有实现Comparable接口,则需要提供Comparator接口。

Comparable接口使用示例

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
42
43
public class ComparableTest {
public static void main(String[] args) {
List<Student> list = Arrays.asList(
new Student("张三",80),
new Student("李四",67),
new Student("王五",93)
);

// 对学生按成绩排序
Collections.sort(list);
System.out.println(list);
// [Student{name='李四', score=67}, Student{name='张三', score=80}, Student{name='王五', score=93}]
}
}

class Student implements Comparable<Student> {
private String name;
private int score;

@Override
public int compareTo(Student o) {
if (this.score > o.score) {
return 1;
} else if (this.score < o.score) {
return -1;
} else {
return 0;
}
}

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

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

Comparator接口使用示例

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class ComparatorTest {
public static void main(String[] args) {
List<Teacher> list = Arrays.asList(
new Teacher("老王", 30),
new Teacher("老张", 27),
new Teacher("老李", 45)
);

// 对老师按年龄排序
list.sort(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
int age1 = o1.getAge();
int age2 = o2.getAge();
if (age1 > age2) {
return 1;
} else if (age1 < age2) {
return -1;
} else {
return 0;
}
}
});
System.out.println(list);
//[Teacher{name='老张', age=27}, Teacher{name='老王', age=30}, Teacher{name='老李', age=45}]
}
}

class Teacher {
private String name;
private int age;

public Teacher(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", 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;
}
}
````

可以发现,上面两个例子排序的结果都是从小到大,这种排序顺序在Java里叫做自然排序,采取上面的写法就可以了,如果你想要排序的结果是从大到小,只需要交换大于时和小于时的返回值即可。<br>

这时候,你可能就明白了String、Integer等对象为什么能够排序了,打开Integer类的源码,果然Integer类也实现了Comparable接口。<br>
Java中,String、Integer、Double等类,都实现了Comparable接口,而且排序时候的顺序都是自然排序,即从小到大。

### 按字母排序字符串列表的示例

```java
List<String> cities = Arrays.asList("c", "a", "B");
cities.sort(Comparator.naturalOrder());//自然排序(从小到大)
System.out.println(cities);//[B, a, c]

cities.sort(String.CASE_INSENSITIVE_ORDER);// 忽略大小写,自然排序(从小到大)
System.out.println(cities);//[a, B, c]

cities.sort(Comparator.reverseOrder());//反转(从大到小)
System.out.println(cities);//[c, a, B]

Comparator的一些其他技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
List<Teacher> list = Arrays.asList(
new Teacher("a_t", 30),
new Teacher("b_t", 27),
new Teacher("c_t", 45),
new Teacher("d_t", 30)
);

//使用age排序
list.sort(Comparator.comparing(Teacher::getAge));
System.out.println(list);
//[Teacher{name='b_t', age=27}, Teacher{name='a_t', age=30}, Teacher{name='d_t', age=30}, Teacher{name='c_t', age=45}]


//使用age排序,并反转
list.sort(Comparator.comparing(Teacher::getAge).reversed());
System.out.println(list);
//[Teacher{name='c_t', age=45}, Teacher{name='a_t', age=30}, Teacher{name='d_t', age=30}, Teacher{name='b_t', age=27}]


//使用age排序,并反转,当age相同时,使用name排序,并反转
list.sort(Comparator.comparing(Teacher::getAge).reversed().thenComparing(Teacher::getName).reversed());
System.out.println(list);
//[Teacher{name='b_t', age=27}, Teacher{name='d_t', age=30}, Teacher{name='a_t', age=30}, Teacher{name='c_t', age=45}]

注意,Teacher类并没有实现Comparable接口。

上面的一些写法用到了lambda表达式,这样可以让我们的代码更加简洁,还没使用的小伙伴快加入吧!

(完)