Java Collections 类的 shuffle() 和 sort() 方法详解
java.util.Collections 是 Java 集合框架的工具类,提供了大量静态方法操作 Collection 和 List。其中 shuffle() 用于随机打乱列表元素,sort() 用于排序列表元素。
两者都是原地操作(in-place),直接修改传入的 List,不返回新列表。
1. Collections.shuffle() —— 洗牌方法
方法签名(两个重载)
public static void shuffle(List<?> list)
public static void shuffle(List<?> list, Random rnd)
- 第一个:使用默认的随机源(内部使用
Random)。 - 第二个:使用指定的
Random对象,可实现可重复/确定性洗牌(相同 seed 结果完全相同)。
核心算法:Fisher-Yates Shuffle(现代版 / Knuth Shuffle)
- 时间复杂度:O(n)(线性时间)
- 空间复杂度:O(1)(原地交换,仅小量辅助变量)
- 保证所有排列等概率出现。
源码实现(JDK 最新版核心逻辑):
public static void shuffle(List<?> list, Random rnd) {
int size = list.size();
if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
// 小列表 或 ArrayList 等随机访问列表:直接原地交换
for (int i = size; i > 1; i--)
swap(list, i - 1, rnd.nextInt(i));
} else {
// LinkedList 等非随机访问列表:先转数组,打乱后再写回
Object[] arr = list.toArray();
for (int i = size; i > 1; i--)
swap(arr, i - 1, rnd.nextInt(i));
ListIterator it = list.listIterator();
for (Object e : arr) {
it.next();
it.set(e);
}
}
}
(SHUFFLE_THRESHOLD 通常为 5)
经典 Fisher-Yates 过程(从后往前):
- 从最后一个元素开始,到第 2 个元素为止。
- 当前位置
i与[0, i]范围内随机一个位置交换。 - 这样每个元素被交换到的概率完全均匀。
使用示例
import java.util.*;
public class ShuffleDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
System.out.println("原始: " + list);
Collections.shuffle(list);
System.out.println("随机打乱: " + list);
// 确定性打乱(相同 seed 结果相同)
Random rnd = new Random(42);
Collections.shuffle(list, rnd);
System.out.println("固定种子打乱: " + list);
}
}
注意事项:
- 只接受
List,其他集合(如Set、Map)需先转为List。 UnsupportedOperationException:如果列表不支持set操作(如Arrays.asList()返回的固定长度列表)。- 线程不安全,多线程需外部同步。
2. Collections.sort() —— 排序方法
方法签名(两个重载)
public static <T extends Comparable<? super T>> void sort(List<T> list) // 自然顺序
public static <T> void sort(List<T> list, Comparator<? super T> c) // 自定义比较器
内部实现(JDK 8+)
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null); // 自然顺序
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
// List 接口默认方法(实际干活的地方)
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c); // 调用 Arrays.sort(TimSort)
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
- 算法:TimSort(归并排序的优化版,Java 7+ 引入)
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)(临时数组)
- 稳定排序(相等元素保持相对顺序)
- 先转数组 →
Arrays.sort()排序 → 写回列表。
与 Arrays.sort() 的区别
| 特性 | Collections.sort(List) | Arrays.sort(数组) |
|---|---|---|
| 操作对象 | List(ArrayList、LinkedList 等) | 数组(基本类型 + 对象数组) |
| 算法(对象) | TimSort(稳定) | TimSort(稳定) |
| 算法(基本类型) | 不支持 | Dual-Pivot QuickSort(更快,但不稳定) |
| 稳定性 | 稳定 | 对象稳定,基本类型不稳定 |
| 性能 | 稍慢(多一次转数组) | 更快,尤其基本类型 |
| 使用场景 | 动态列表排序 | 数组、追求极致性能 |
使用示例
List<String> list = new ArrayList<>(Arrays.asList("Geeks", "For", "Geeks", "Dear", "Is", "Superb"));
// 1. 自然升序(Comparable)
Collections.sort(list);
System.out.println("升序: " + list); // [Dear, For, Geeks, Geeks, Is, Superb]
// 2. 降序(Comparator)
Collections.sort(list, Collections.reverseOrder());
System.out.println("降序: " + list); // [Superb, Is, Geeks, Geeks, For, Dear]
// 3. 自定义 Comparator(按长度排序)
Collections.sort(list, (a, b) -> a.length() - b.length());
System.out.println("按长度: " + list);
自定义对象排序:
class Student implements Comparable<Student> {
int id;
String name;
// 实现 compareTo...
}
Collections.sort(students); // 自然顺序
Collections.sort(students, new SortByName()); // 或 Comparator
异常:
ClassCastException:元素未实现Comparable且未传Comparator。NullPointerException:列表或 Comparator 为 null。
总结对比
| 方法 | 作用 | 算法 | 时间复杂度 | 是否稳定 | 适用类型 | 推荐场景 |
|---|---|---|---|---|---|---|
shuffle() | 随机打乱 | Fisher-Yates | O(n) | – | List | 抽奖、随机队列、洗牌 |
sort() | 排序 | TimSort | O(n log n) | 是 | List | 需要稳定排序的业务场景 |
Arrays.sort() | 排序 | Dual-Pivot QS / TimSort | O(n log n) | 基本类型否 | 数组 | 高性能、基本类型数组 |
一句话记忆:
- 要随机 →
Collections.shuffle(list) - 要有序 →
Collections.sort(list)或list.sort(...)(JDK 8+ 推荐)
需要完整可运行的 Demo 项目、自定义对象排序案例、或与 Stream API 结合的写法,随时告诉我,我可以继续补充!