本文目录导读:

- 归集到集合(toList/toSet/toCollection)
- 字符串连接(joining)
- 分组(groupingBy)
- 分区(partitioningBy)
- 聚合计算(summarizing / averaging / summing)
- 归约(reducing)
- 映射(mapping)
- 收集到 Map(toMap)
- 计数与极值(counting / maxBy / minBy)
- 自定义收集(collectingAndThen)
- 总结表
Java 8 中引入的 Collectors 是一个非常强大的工具类,主要用于将 Stream 中的元素累积到各种容器(如 List、Set、Map)中,或者进行各种归约操作(如求和、分组、连接字符串)。
以下是 Collectors 最常用的十大功能分类及代码示例:
归集到集合(toList/toSet/toCollection)
将流中的数据收集到常见的集合中。
import java.util.stream.Collectors; import java.util.*; // toList: 默认使用 ArrayList List<String> list = stream.collect(Collectors.toList()); // toSet: 去重,默认使用 HashSet Set<String> set = stream.collect(Collectors.toSet()); // toCollection: 指定具体的集合类型(如 TreeSet, LinkedList) TreeSet<String> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
字符串连接(joining)
将流中的元素(通常是字符串)连接成一个字符串,可以指定分隔符、前缀和后缀。
List<String> words = Arrays.asList("Hello", "World", "Java");
// 简单连接: "HelloWorldJava"
String s1 = words.stream().collect(Collectors.joining());
// 带分隔符: "Hello, World, Java"
String s2 = words.stream().collect(Collectors.joining(", "));
// 带分隔符、前缀、后缀: "[Hello, World, Java]"
String s3 = words.stream().collect(Collectors.joining(", ", "[", "]"));
分组(groupingBy)
类似 SQL 中的 GROUP BY,根据属性将元素分组,返回 Map<K, List<V>>。
// 假设有一个 Person 类,包含 age 和 name
List<Person> people = ...;
// 按年龄分组: Map<Integer, List<Person>>
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
// 分组后计数: Map<Integer, Long> (每个年龄有多少人)
Map<Integer, Long> ageCount = people.stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));
// 分组后映射: 按年龄分组,只取名字: Map<Integer, List<String>>
Map<Integer, List<String>> namesByAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge,
Collectors.mapping(Person::getName, Collectors.toList())));
分区(partitioningBy)
是 groupingBy 的特殊情况,根据 boolean 条件将数据分为两组(true 和 false)。
// 按年龄是否大于18分区
Map<Boolean, List<Person>> partitioned = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() >= 18));
// 获取成年人的列表
List<Person> adults = partitioned.get(true);
聚合计算(summarizing / averaging / summing)
用于计算数值型属性的统计量。
// 求总和: int/ long/ double
int totalAge = people.stream().collect(Collectors.summingInt(Person::getAge));
// 求平均值: double
double avgAge = people.stream().collect(Collectors.averagingInt(Person::getAge));
// 一次性获取 计数、总和、最小、最大、平均: IntSummaryStatistics
IntSummaryStatistics stats = people.stream()
.collect(Collectors.summarizingInt(Person::getAge));
// stats.getCount(); stats.getSum(); stats.getMin(); stats.getMax(); stats.getAverage();
归约(reducing)
这是 Collectors 中最底层、最通用的归约操作,其他如 summing 等通常基于它实现。
// 求所有年龄的总和
Optional<Integer> total = people.stream()
.map(Person::getAge)
.collect(Collectors.reducing((a, b) -> a + b));
// 带初始值的归约
Integer totalWithInit = people.stream()
.map(Person::getAge)
.collect(Collectors.reducing(0, (a, b) -> a + b));
映射(mapping)
用于对元素进行转换后再收集,常配合其他 Collector 使用。
// 将所有 Person 的名字转为大写并收集到 List
List<String> upperNames = people.stream()
.collect(Collectors.mapping(
p -> p.getName().toUpperCase(),
Collectors.toList()
));
收集到 Map(toMap)
将 Stream 转换为 Map,需要指定 Key 和 Value 的提取函数。需要小心 key 冲突。
// 将 Person 对象的 id 作为 key,name 作为 value
Map<Integer, String> idToName = people.stream()
.collect(Collectors.toMap(
Person::getId, // key 提取
Person::getName // value 提取
));
// 处理 key 冲突: 如果两个 Person id 相同,保留第一个
Map<Integer, Person> idToPerson = people.stream()
.collect(Collectors.toMap(
Person::getId,
Function.identity(), // 元素本身作为value
(existing, replacement) -> existing // 冲突时保留已有
));
计数与极值(counting / maxBy / minBy)
// 计数: long
long count = people.stream().collect(Collectors.counting());
// 找年龄最大的人: Optional<Person>
Optional<Person> oldest = people.stream()
.collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge)));
// 找年龄最小的人
Optional<Person> youngest = people.stream()
.collect(Collectors.minBy(Comparator.comparingInt(Person::getAge)));
自定义收集(collectingAndThen)
在收集完成后,对结果再做一次额外处理。
// 收集为 List,然后转为不可变集合
List<String> unmodifiableList = words.stream()
.collect(Collectors.collectingAndThen(
Collectors.toList(), // 先收集成 List
Collections::unmodifiableList // 再包装为不可变
));
// 分组后统计每组数量,并返回一个不可变 Map
Map<Integer, Long> immutableCount = people.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Person::getAge, Collectors.counting()),
Collections::unmodifiableMap
));
总结表
| 功能 | 方法名 | 典型返回类型 |
|---|---|---|
| 转 List | toList() |
List<T> |
| 转 Set | toSet() |
Set<T> |
| 转指定类型 | toCollection() |
Collection<T> |
| 字符串连接 | joining() |
String |
| 分组 | groupingBy() |
Map<K, List<V>> |
| 分区 | partitioningBy() |
Map<Boolean, List<V>> |
| 数值求和 | summingInt/Long/Double() |
int/long/double |
| 数值统计 | summarizingInt() |
IntSummaryStatistics |
| 归约 | reducing() |
Optional<T> 或 T |
| 映射 | mapping() |
取决于下游收集器 |
| 转 Map | toMap() |
Map<K, V> |
| 计数 | counting() |
Long |
| 最大/最小 | maxBy()/minBy() |
Optional<T> |
| 后处理 | collectingAndThen() |
取决于处理函数 |
这些功能基本覆盖了日常开发中 90% 以上的 Stream 收集需求,熟练掌握可以大大简化集合操作的代码量。