Java 中排序的稳定性详解
1. 什么是排序稳定性?
排序稳定性是指:如果两个相等的元素在排序前的相对顺序,在排序后保持不变,那么这个排序算法就是稳定的。
示例:
排序前:[(A, 2), (B, 1), (C, 2), (D, 3)]
按数字排序后:
稳定排序:[(B, 1), (A, 2), (C, 2), (D, 3)] // A 仍在 C 前面
不稳定排序:[(B, 1), (C, 2), (A, 2), (D, 3)] // A 和 C 的相对顺序改变
2. Java 中常用排序算法的稳定性
2.1 稳定排序算法
| 算法 | 使用场景 | 稳定性 |
|---|
| 归并排序 | Arrays.sort(Object[])
Collections.sort() | 稳定 |
| TimSort | Java 7+ 的对象排序 | 稳定 |
| 插入排序 | 小数组或部分有序 | 稳定 |
| 冒泡排序 | 教学用途 | 稳定 |
2.2 不稳定排序算法
| 算法 | 使用场景 | 稳定性 |
|---|
| 快速排序 | Arrays.sort(int[]) | 不稳定 |
| 堆排序 | PriorityQueue | 不稳定 |
| 选择排序 | 教学用途 | 不稳定 |
3. Java 具体排序方法的稳定性
3.1 Arrays.sort()
int[] intArray = {5, 2, 5, 1, 3};
Arrays.sort(intArray);
Integer[] objArray = {5, 2, 5, 1, 3};
Arrays.sort(objArray);
Person[] people = {new Person("Alice", 25), new Person("Bob", 25)};
Arrays.sort(people, Comparator.comparing(Person::getAge));
3.2 Collections.sort()
List<Integer> list = Arrays.asList(5, 2, 5, 1, 3);
Collections.sort(list);
List<Person> peopleList = new ArrayList<>();
Collections.sort(peopleList, Comparator.comparing(Person::getAge));
3.3 Stream API 排序
List<Person> sorted = people.stream()
.sorted(Comparator.comparing(Person::getAge)
.thenComparing(Person::getName))
.collect(Collectors.toList());
4. 实际应用示例
4.1 多级排序
class Student {
String name;
int grade;
int age;
public static void stableMultiSort(List<Student> students) {
Collections.sort(students, Comparator.comparingInt(s -> s.age));
Collections.sort(students, Comparator.comparingInt(s -> s.grade));
Collections.sort(students,
Comparator.comparingInt((Student s) -> s.grade)
.thenComparingInt(s -> s.age));
}
}
4.2 何时需要稳定排序?
- 多关键字排序:先按次要关键字排序,再按主要关键字排序
- 用户界面:保持用户熟悉的顺序
- 版本控制:保持历史记录的相对顺序
- 事务处理:保持时间戳顺序
5. 自定义稳定排序示例
public class StableSortExample {
static class Employee {
String name;
String department;
LocalDate hireDate;
public static void sortEmployees(List<Employee> employees) {
Collections.sort(employees,
Comparator.comparing((Employee e) -> e.department)
.thenComparing(e -> e.hireDate));
}
}
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", "IT", LocalDate.of(2020, 1, 1)),
new Employee("Bob", "HR", LocalDate.of(2019, 6, 1)),
new Employee("Charlie", "IT", LocalDate.of(2021, 3, 1))
);
Employee.sortEmployees(employees);
}
}
6. 性能考虑
| 排序类型 | 平均时间复杂度 | 空间复杂度 | 稳定性 |
|---|
| TimSort (Java对象) | O(n log n) | O(n) | 稳定 |
| 双轴快速排序 (基本类型) | O(n log n) | O(log n) | 不稳定 |
| 插入排序 | O(n²) | O(1) | 稳定 |
7. 最佳实践建议
- 默认使用稳定排序:
Collections.sort() 和对象数组的 Arrays.sort()
- 基本类型性能优先:对基本类型数组使用
Arrays.sort(),不关心稳定性
- 多级排序:使用
Comparator.thenComparing() 链
- 自定义比较器:确保比较逻辑与稳定性需求一致
- 文档说明:在API文档中说明排序的稳定性特性
总结
- Java 中对象排序默认是稳定的(使用 TimSort)
- 基本类型排序不稳定(出于性能考虑)
- 稳定排序在多关键字排序场景中非常重要
- 了解排序稳定性有助于写出更可预测的代码