Java 学习:Comparable 与 Comparator 接口排序逻辑详解
本文对比了 Java 中 Comparable 和 Comparator 两个接口的区别。Comparable 用于定义对象内部的默认排序规则,实现后类可直接排序;Comparator 用于外部自定义排序规则,灵活性强,支持多种排序方式且无需修改原类代码。通过代码示例展示了两者在 Arrays.sort 中的使用方法及核心差异,帮助开发者根据场景选择合适的排序策略。

本文对比了 Java 中 Comparable 和 Comparator 两个接口的区别。Comparable 用于定义对象内部的默认排序规则,实现后类可直接排序;Comparator 用于外部自定义排序规则,灵活性强,支持多种排序方式且无需修改原类代码。通过代码示例展示了两者在 Arrays.sort 中的使用方法及核心差异,帮助开发者根据场景选择合适的排序策略。

先看两组代码:
代码 1:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// 创建一个数组
String[] strs = {"李华", "小明", "小红"};
// 对数组进行排序
Arrays.sort(strs);
// 打印
System.out.println(Arrays.toString(strs));
}
}
上述代码可以打印出比较后的顺序。
代码 2:
import java.util.Arrays;
class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Student[] stus = {
new Student("小明", 19),
new Student("小红", 20),
new Student("小刚", 18)
};
// 对数组进行排序
Arrays.sort(stus);
// 打印
System.out.println(Arrays.toString(stus));
}
}
上述的代码发生报错:

代码如下:
private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi) return 1;
// Find end of run, and reverse range if descending
// 解释:
// a 是引用变量,此时是 Student 类型的引用变量
// Comparable 强制把 a 引用变量转化成 comparable 类型,
// 但是我们写的 Student 并没有实现接口 Comparable ,所以发生报错
if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) {
// Descending
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0) runHi++;
reverseRange(a, lo, runHi);
} else {
// Ascending
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0) runHi++;
}
return runHi - lo;
}
在源代码中,使用 Array.sort() 会把引用类型强制转化成 Comparable。
(Comparable) a[runHi++] 强制把 a 引用变量转化成 Comparable 类型,但是我们写的 Student 并没有实现接口 Comparable,所以发生报错。
那么,String 是不是实现了 Comparable 接口呢?
看一看 String 的源代码:

那为什么都必须实现这个接口呢?
在讲解接口的时已经说过,接口是一个标准,大家都执行这个标准,设计出的程序才能通用。
如:String 实现了 Comparable 接口,它可以用 Arrays.sort() 进行快速排序。
特点: (1)用于定义默认的排序规则。 (2)实现了 Comparable 的类对象可以直接通过 Collections.sort() 或 Arrays.sort() 方法进行排序。 (3)它属于对象本身的一部分,默认定义了对象之间的比较逻辑。
Comparable 接口中只有一个方法:
int compareTo(T o);
方法参数与返回值: (1)参数:T o,表示要比较的对象。 (2)返回值:
语法:
public class 类名 implements Comparable<类名> {
@Override
public int compareTo(参数) {
// ...
return 返回值;
}
}
示例:
// Comparable<> 什么类就需要填什么
public class Student implements Comparable<Student> {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
// Comparable 接口只有此一个方法,实现 compareTo 方法
@Override
public int compareTo(Student other) {
return this.score - other.score; // 按照分数升序排序
// return other.score - this.score 按照分数降序排序
}
}
使用 compareTo 函数:
import java.util.Arrays;
class Student implements Comparable<Student> {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student s) {
return this.age - s.age;
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student("小明", 19);
Student s2 = new Student("小红", 20);
System.out.println(s1.compareTo(s2));
}
}
返回的是 19 - 20 的值:

使用 Arrays.sort() 按照年龄升序排序:
import java.util.Arrays;
class Student implements Comparable<Student> {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student s) {
return this.age - s.age; // 升序排序
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
public class Main {
public static void main(String[] args) {
Student[] stus = {
new Student("小明", 19),
new Student("小红", 20),
new Student("小刚", 18)
};
// 排序前
System.out.println( + Arrays.toString(stus));
Arrays.sort(stus);
System.out.println( + Arrays.toString(stus));
}
}

Arrays.sort() 排序使用 Student 中的 compareTo 函数了吗?我们怎么知道呢?
在 compareTo 中添加一个打印语句:
@Override
public int compareTo(Student s) {
System.out.println("comppareTo()");
return this.age - s.age;
}
再次运行,结果:

很明显,使用 Arrays.sort() 时调用了 compareTo 函数。
一个有年龄排序方法的类:
class Student implements Comparable<Student> {
public String name;
public int age;
public int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public int compareTo(Student s) {
return this.age - s.age; // 升序排序
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
Comparable 接口只能被重写一次,只能进行年龄比较,但是,我们要想进行排序的是分数 score,怎么办呢?
此时,Comparator 接口可以解决这个问题。Comparator 有很强的灵活性,可以设计比较任意的属性。
特点:
(1)用于定义自定义的排序规则。
(2)它是一个独立的比较器,与对象本身的定义无关。
(3)当需要对同一类的对象应用多种排序规则时,Comparator 更加灵活。
(4)Comparator 的对象可以传递给 Collections.sort() 或 Arrays.sort() 方法。
Comparator 接口中有两个常用方法:
int compare(T o1, T o2);
boolean equals(Object obj); // 判断是否与另一个比较器相等(通常很少需要重写)
语法:
public class 类名 implements Comparator<需要比较类的类> {
@Override
public int compare(参数) {
// ...
return 返回值;
}
}
Student 类:
public class Student {
public String name;
public int age;
public int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
比较类:
import java.util.Comparator;
// 注意,这里的类需要填进行比较的类
class ScoreSort implements Comparator<Student> {
// 重写比较函数
// 注意,这里的参数有两个
@Override
public int compare(Student s1, Student s2) {
return s1.score - s2.score;
}
}
两个对象的 score 进行比较:
public class Main {
public static void main(String[] args) {
// 创建两个对象
Student s1 = new Student("小明", 19, 80);
Student s2 = new Student("小红", 20, 70);
// 创建一个排序对象
ScoreSort ss = new ScoreSort();
// 打印出比较的结果
System.out.println(ss.compare(s1, s2));
}
}
结果:

Arrays.sort() 可以填入两个参数,只需要把 Arrays.sort(array, object) 中的 object 换成排序对象即可。
public class Main {
public static void main(String[] args) {
Student[] stus = {
new Student("小明", 19, 80),
new Student("小红", 20, 70),
new Student("小刚", 18, 90)
};
// 排序前
System.out.println("排序前:" + Arrays.toString(stus));
// 创建排序的对象
ScoreSort scoreSort = new ScoreSort();
// 对数组进行排序
Arrays.sort(stus, scoreSort); // 需要添加排序对象
// 排序后
System.out.println("排序后" + Arrays.toString(stus));
}
}
结果:

Comparable 和 Comparator 的核心区别
| 特性 | Comparable | Comparator |
|---|---|---|
| 用途 | 定义对象的默认排序规则 | 定义自定义的排序规则 |
| 位置 | 排序逻辑在对象内部实现 | 排序逻辑在外部定义 |
| 接口方法 | compareTo(T o) | compare(T o1, T o2) |
| 实现方式 | 对象类实现 Comparable 接口 | 通过实现 Comparator 接口创建比较器 |
| 灵活性 | 只能定义一种排序规则 | 可以定义多种排序规则 |
| 使用场景 | 对象有一个自然排序 | 对象需要多种排序规则或自定义排序 |
| 修改类代码的需求 | 必须修改类的代码以实现接口 | 不需要修改类的代码,可在外部定义排序逻辑 |
选择 Comparable 还是 Comparator?
| 使用场景 | 选择 |
|---|---|
| 类的排序逻辑是固定的,且只有一种排序方式。 | Comparable |
| 类的排序逻辑可能有多种(如按年龄、按名字)。 | Comparator |
| 类的代码无法修改(如第三方库类)。 | Comparator |
| 需要更灵活的排序方式。 | Comparator |

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online