java 排序 详解
Java 提供了多种方式对数据进行排序,包括数组和集合的排序。排序在日常开发中非常常见,以下将从排序算法的基本原理、Java 中的内置排序方法以及自定义排序三方面进行详解。
1. 排序的基本概念
排序是将一组数据按特定顺序排列的过程,常见顺序包括:
- 升序:从小到大排列(如:1, 2, 3, …)。
- 降序:从大到小排列(如:10, 9, 8, …)。
常见排序算法及其时间复杂度
排序算法 | 时间复杂度(平均) | 时间复杂度(最坏) | 空间复杂度 | 稳定性 |
---|---|---|---|---|
冒泡排序 (Bubble Sort) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 稳定 |
插入排序 (Insertion Sort) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 稳定 |
选择排序 (Selection Sort) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 不稳定 |
快速排序 (Quick Sort) | O ( n log n ) O(n \log n) O(nlogn) | O ( n 2 ) O(n^2) O(n2) | O ( log n ) O(\log n) O(logn) | 不稳定 |
归并排序 (Merge Sort) | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( n ) O(n) O(n) | 稳定 |
堆排序 (Heap Sort) | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( 1 ) O(1) O(1) | 不稳定 |
2. Java 中的内置排序方法
Java 提供了丰富的内置排序方法,主要通过 Arrays
和 Collections
两个工具类实现。
(1) 使用 Arrays.sort
方法(针对数组)
用法
- 基础类型数组:直接使用。
- 引用类型数组:可以传入自定义比较器。
示例代码
import java.util.Arrays;
public class ArraySortExample {
public static void main(String[] args) {
// 基础类型数组排序
int[] numbers = {5, 2, 8, 1, 3};
Arrays.sort(numbers); // 默认升序
System.out.println("基础类型排序后:" + Arrays.toString(numbers));
// 引用类型数组排序
String[] words = {"apple", "banana", "cherry", "date"};
Arrays.sort(words); // 默认按字典序排序
System.out.println("引用类型排序后:" + Arrays.toString(words));
// 自定义排序(降序)
Arrays.sort(words, (a, b) -> b.compareTo(a));
System.out.println("自定义排序后:" + Arrays.toString(words));
}
}
输出结果
基础类型排序后:[1, 2, 3, 5, 8]
引用类型排序后:[apple, banana, cherry, date]
自定义排序后:[date, cherry, banana, apple]
特点
Arrays.sort
使用 双轴快速排序(Dual-Pivot Quicksort)实现,时间复杂度为 O ( n log n ) O(n \log n) O(nlogn)。- 对基础类型排序效率极高,但对引用类型需要更多内存。
(2) 使用 Collections.sort
方法(针对集合)
用法
- 专为
List
设计(如ArrayList
、LinkedList
等)。 - 可以使用默认排序(元素需实现
Comparable
接口)或自定义比较器。
示例代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionSortExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
numbers.add(3);
// 默认升序排序
Collections.sort(numbers);
System.out.println("默认排序后:" + numbers);
// 自定义排序(降序)
Collections.sort(numbers, (a, b) -> b - a);
System.out.println("自定义排序后:" + numbers);
}
}
输出结果
默认排序后:[1, 2, 3, 5, 8]
自定义排序后:[8, 5, 3, 2, 1]
特点
Collections.sort
内部调用List
的sort
方法,底层使用 TimSort 算法。- 稳定排序,适合复杂对象的排序。
(3) 使用 List.sort
方法
从 Java 8 开始,List
接口新增了 sort
方法,可以直接传入比较器。
示例代码
import java.util.ArrayList;
import java.util.List;
public class ListSortExample {
public static void main(String[] args) {
List<String> words = new ArrayList<>();
words.add("apple");
words.add("banana");
words.add("cherry");
words.add("date");
// 默认升序
words.sort(String::compareTo);
System.out.println("默认排序后:" + words);
// 自定义排序(按长度降序)
words.sort((a, b) -> b.length() - a.length());
System.out.println("按长度降序排序后:" + words);
}
}
输出结果
默认排序后:[apple, banana, cherry, date]
按长度降序排序后:[banana, cherry, apple, date]
3. 自定义排序
对于复杂对象,需要使用 Comparable
或 Comparator
来定义排序规则。
(1) 使用 Comparable
接口
Comparable
接口用于定义自然排序,需实现其 compareTo
方法。
示例代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Student implements Comparable<Student> {
String name;
int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public int compareTo(Student other) {
return this.score - other.score; // 按分数升序
}
@Override
public String toString() {
return name + ": " + score;
}
}
public class ComparableExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 85));
students.add(new Student("Bob", 92));
students.add(new Student("Charlie", 78));
Collections.sort(students);
System.out.println("按分数升序排序:" + students);
}
}
输出结果
按分数升序排序:[Charlie: 78, Alice: 85, Bob: 92]
(2) 使用 Comparator
接口
Comparator
接口用于定义外部排序规则,可以灵活调整排序逻辑。
示例代码
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class Student {
String name;
int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return name + ": " + score;
}
}
public class ComparatorExample {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 85));
students.add(new Student("Bob", 92));
students.add(new Student("Charlie", 78));
// 按分数降序排序
students.sort((a, b) -> b.score - a.score);
System.out.println("按分数降序排序:" + students);
// 按名字字母升序排序
students.sort(Comparator.comparing(s -> s.name));
System.out.println("按名字升序排序:" + students);
}
}
输出结果
按分数降序排序:[Bob: 92, Alice: 85, Charlie: 78]
按名字升序排序:[Alice: 85, Bob: 92, Charlie: 78]
4. 常见排序陷阱与优化
- 稳定性问题:
- 对于需要保持原始顺序的排序,使用稳定排序算法(如
Collections.sort
和TimSort
)。
- 对于需要保持原始顺序的排序,使用稳定排序算法(如
- 性能优化:
- 对小规模数组使用插入排序或冒泡排序。
- 对大规模数据使用快速排序或归并排序。
- **
避免多次比较**:- 使用
Comparator.comparing
链式调用时避免重复字段比较。
- 使用
5. 总结
- 数组排序:使用
Arrays.sort
,适合基础类型和简单对象。 - 集合排序:使用
Collections.sort
或List.sort
,适合复杂对象和灵活排序需求。 - 自定义排序:通过
Comparable
和Comparator
实现,灵活定义规则。 - 排序算法:根据数据规模和需求选择合适的排序算法。
Java 内置的排序方法效率高、使用方便,但理解其底层原理和优化策略可以帮助开发者更好地应对复杂排序需求。