Collections 工具类
本教程共 100 篇 · 第 80 篇 · 更新于 2026-08-05 · 约 5 分钟阅读
本节目标:认识专门服务于集合的
Collections工具类(注意和 Collection 接口的大小写差别),学完能用它排序、反转、查最值、生成不可变集合。
先分清两个长得很像的名字
Collection 是接口(单列集合的根),Collections 是个工具类,里面全是 static 方法,用来操作各种集合。大写小写的差别,初学者经常看花眼。
List<Integer> list = new ArrayList<>(List.of(3, 1, 2));
Collections.sort(list); // 排序,注意是 Collections 不是 Collection
System.out.println(list); // [1, 2, 3]
Note
Collections.sort(list)会原地修改原列表;如果你不想改原列表,可以先new ArrayList<>(list)拷贝一份再排。Java 8 起List接口自己也提供了list.sort(...)方法,效果一样,看个人习惯用哪个。
最常用的算法方法
这些方法是日常最高频的:
Collections.sort(list); // 升序排序(元素需实现 Comparable)
Collections.sort(list, Comparator.reverseOrder()); // 降序
Collections.reverse(list); // 反转顺序
Collections.shuffle(list); // 随机打乱(洗牌)
Collections.fill(list, 0); // 把所有元素填成同一个值
Integer max = Collections.max(list); // 取最大
Integer min = Collections.min(list); // 取最小
int idx = Collections.binarySearch(list, 2); // 二分查找,前提已排序,找不到返回负数
binarySearch 只有在列表已经排好序时才准,否则结果无意义。它比逐个遍历快得多,是「先排序、再查找」场景的标配。
拷贝和交换
List<Integer> src = List.of(1, 2, 3);
List<Integer> dst = new ArrayList<>(Arrays.asList(0, 0, 0));
Collections.copy(dst, src); // 把 src 拷进 dst(dst 必须够长)
Collections.swap(list, 0, 2); // 交换下标 0 和 2 的元素
Warning
Collections.copy(dst, src)要求目标列表长度不小于源列表,否则抛IndexOutOfBoundsException。它不是「复制出一个新列表」,而是往已有列表里覆盖,别和new ArrayList<>(src)搞混。
生成特殊集合
Collections 还提供一批返回「特殊集合」的工厂方法,常用于写只读接口、测试桩:
List<String> empty = Collections.emptyList(); // 空且不可变
Set<Integer> one = Collections.singleton(42); // 只含一个元素,不可变
List<String> fixed = Collections.unmodifiableList(list); // 包装成只读,改会抛异常
unmodifiableList 返回的只是「视图」,底层还是那个 list,你改原来的 list,视图里也会变。它挡的是「通过视图去改」,不是「从源头改」。
Tip想真正拿到一份不可变副本,优先用 Java 9 的
List.copyOf(list)/Set.copyOf/Map.copyOf,比unmodifiableList(new ArrayList<>(...))写法更短更清晰。
synchronized 包装是历史兼容
Collections 有一组 synchronizedList / synchronizedMap / synchronizedSet,给集合套上同步锁,让它在多线程下「勉强安全」。
List<String> safe = Collections.synchronizedList(new ArrayList<>());
Warning这套同步包装只是历史兼容方案,性能一般,而且迭代时还得自己手动加锁才真安全。现代多线程场景请用
java.util.concurrent里的ConcurrentHashMap、CopyOnWriteArrayList等专门实现,别拿 synchronized 包装凑合。
sort 的两种传参
除了按自然顺序排,Collections.sort 还能接收 Comparator 自定义规则:
List<String> words = new ArrayList<>(List.of("ccc", "a", "bb"));
Collections.sort(words, Comparator.comparing(String::length)); // 按长度排
其实 List 接口自己的 sort 方法也一样(words.sort(...))。两者选哪个都行,团队统一风格即可。Comparator 的写法详见第 82 章,这里先用起来感受一下。
max/min 也能自定义
max/min 默认按自然顺序取最值,也能传 Comparator:
List<User> users = ...;
User oldest = Collections.max(users, Comparator.comparingInt(User::age)); // 年龄最大
这让你「按某个字段取最大」变得一行搞定,比如从一堆用户里挑年龄最大的、从一堆商品里挑最贵的,不用自己写循环比大小。
二分查找的前提
binarySearch 要求列表「已经按相同比较规则排好序」。如果排序用了 Comparator,查找也要传同一个 Comparator,否则结果不可信。找不到时返回的是「插入点的负数」(不是简单的 -1),需要时再查文档细节,日常用得不多。
这些方法和 Stream 的关系
Java 8 起集合还能用 Stream(list.stream().sorted()...)做同样的事,功能更强。本章的 Collections 方法是「经典命令式」写法,先掌握它们,日后学 Stream 会轻松很多。两者不冲突,按场景挑——简单排序用 Collections,链式复杂处理用 Stream。
空集合和单元素集合的实战价值
Collections.emptyList() 看着没用,其实在写方法返回值时很香:当没数据要返回时,直接返回空集合而不是 null,调用方就不用判空,for-each 遍历空集合也不会报错。
List<String> find(String key) {
if (!store.containsKey(key)) {
return Collections.emptyList(); // 比返回 null 友好得多
}
return store.get(key);
}
singleton(x) 则常用于把单个元素当集合传进去,比如 list.removeAll(Collections.singleton("x")) 一次性删掉所有等于 x 的元素,比手写循环简洁。
同步包装的真相
前面提过 synchronizedList 只是历史兼容。它确实让单操作线程安全,但「检查再操作」(如先 contains 再 add)这种复合动作仍然不安全,得你自己加锁。现代并发请用 java.util.concurrent 包,那里有真正为并发设计的实现。初学阶段你写的几乎都是单线程程序,了解有这回事即可,别急着往项目里塞同步包装。
小提醒:sort 是原地操作
Collections.sort 会直接改动你传进去的列表,而不是返回新列表。如果你不想破坏原数据,先拷贝:new ArrayList<>(list) 再排。很多人误以为 sort 返回排好序的新列表,结果原列表被改、返回值又没接,逻辑就乱了。记住它和 shuffle 一样都是原地修改,不会返回新列表。这个特性和 Arrays.sort 对数组的处理一致,牢记「排序即修改原集合」能让你少踩很多坑。
shuffle 的随机性
shuffle 用的是洗牌算法,每次结果不同,适合随机抽题、随机排序展示等场景。如果你想结果可复现(比如写测试),可以用带 Random 参数的重载 shuffle(list, random) 固定种子,这样每次跑出来顺序都一样,方便断言。
小结
Collections 是操作集合的工具类,提供排序、反转、洗牌、最值、二分查找、拷贝等算法方法,也能生成空集合、单元素集合和只读视图。它的大小写和 Collection 接口不同,别看错。下一章看遍历集合的底层机制——迭代器。