收集器 -- java.util.stream.Collectors
Collectors是 Stream API 的核心工具类,提供了一系列静态工厂方法,用于将 Stream 流中的元素收集为集合(List/Set/Map)、值(求和 / 计数 / 最值)、字符串等,支持并行处理,且用法灵活。
- 基础收集:转为集合(List/Set/Map/ 数组)
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| toList() | 收集为List(jdk8中默认为Arraylist,线程不安全 ) | List list = stream.collect(Collectors.toList()); |
| toSet() | 收集为 Set(默认是 HashSet,去重且无序) | Set set = stream.collect(Collectors.toSet()); |
| toCollection(Supplier collectionFactory) | 收集为自定义集合(如 LinkedList、TreeSet) | LinkedList linkedList = stream.collect(Collectors.toCollection(LinkedList::new));TreeSet treeSet = stream.collect(Collectors.toCollection(TreeSet::new)); |
| toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) | 收集为 Map(key 由 keyMapper 生成,value 由 valueMapper 生成) | 示例:将用户列表转为「用户 ID→用户名」的 MapMap<Long, String> userMap = users.stream().collect(Collectors.toMap(User::getId, User::getName)); |
| toMap(..., BinaryOperator++mergeFunction)++ | 重载:解决 key 冲突(如两个元素生成相同 key 时,用 mergeFunction 合并 value) | 示例:key 冲突时取后者的值Map<Long, String> userMap = users.stream().collect(Collectors.toMap(User::getId, User::getName, (v1, v2) -> v2)); |
| toMap(..., Supplier mapFactory) | 重载:指定 Map 实现(如 LinkedHashMap 保留插入顺序) | Map<Long, String> linkedMap = users.stream().collect(Collectors.toMap(User::getId, User::getName, (v1, v2) -> v2, LinkedHashMap::new)); |
| toConcurrentMap(...) | 收集为并发安全的 Map(ConcurrentHashMap),用法同 toMap | ConcurrentMap<Long, String> concurrentMap = users.stream().collect(Collectors.toConcurrentMap(User::getId, User::getName)); |
| toArray(IntFunction<A[]> generator) | 收集为指定类型的数组 | String[] arr = stream.collect(Collectors.toArray(String[]::new)); |
- 聚合计算:求和/计数/最值/平均值
用于对 Stream 中的数值型元素(或可转为数值的元素)进行统计计算,返回单个结果。
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| counting() | 统计元素个数(等价于 stream.count(),但可作为复合收集器的一部分) | long count = stream.collect(Collectors.counting()); |
| summingInt(ToIntFunction<? super T> mapper) | 按 mapper 转为 int 后求和(同理:summingLong/summingDouble) | 示例:计算所有用户年龄总和: int totalAge = users.stream().collect(Collectors.summingInt(User::getAge)); |
| averagingInt(ToIntFunction<? super T> mapper) | 按 mapper 转为 int 后求平均值(返回 double,同理:averagingLong/averagingDouble) | double avgAge = users.stream().collect(Collectors.averagingInt(User::getAge)); |
| summarizingInt(ToIntFunction<? super T> mapper) | 一次性获取总和、平均值、最值、个数(返回 IntSummaryStatistics,同理:summarizingLong/summarizingDouble) | 示例:获取年龄统计信息: IntSummaryStatistics stats = users.stream().collect(Collectors.summarizingInt(User::getAge));System.out.println("总和:" + stats.getSum() + ",平均值:" + stats.getAverage()); |
| maxBy(Comparator<? super T> comparator) | 按比较器获取最大值(返回 Optional,避免空指针) | 示例:获取年龄最大的用户: Optional maxAgeUser = users.stream().collect(Collectors.maxBy(Comparator.comparingInt(User::getAge))); |
| minBy(Comparator<? super T> comparator) | 按比较器获取最小值(返回 Optional) | Optional minAgeUser = users.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getAge))); |
- 分组与分区:按条件分类元素
1、分组(groupingBy):按任意条件分组(多组):将Stream元素按指定维度分组,结果为Map<分组键, 分组后的元素集合>。
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| groupingBy(Function<? super T,? extends K> classifier) | 按 classifier 生成的键分组,默认值为 List | 示例:按用户性别分组:Map<String, List> groupByGender = users.stream().collect(Collectors.groupingBy(User::getGender)); |
| groupingBy(..., Supplier mapFactory) | 重载:指定分组结果的 Map 实现(如 LinkedHashMap 保留分组顺序) | Map<String, List> linkedGroup = users.stream().collect(Collectors.groupingBy(User::getGender, LinkedHashMap::new));需要 jdk16+ |
| groupingBy(..., Collector<? super T,A,D> downstream) | 重载:多级分组 / 分组后聚合(downstream 是分组后的二次收集器) | 示例 2:按性别分组后,收集每组用户名(二级收集):Map<String, List> genderNames = users.stream().collect(Collectors.groupingBy(User::getGender, Collectors.mapping(User::getName, Collectors.toList()))); |
| groupingByConcurrent(...) | 并行分组(返回 ConcurrentMap,适合大规模并行流) | ConcurrentMap<String, List> concurrentGroup = users.parallelStream().collect(Collectors.groupingByConcurrent(User::getGender)); |
2、分区(partitioningBy):按布尔条件分组(两组:true/false):是 groupingBy 的特殊情况,分组键固定为 Boolean,结果仅含两个分组(满足条件 / 不满足条件)。
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| partitioningBy(Predicate<? super T> predicate) | 按 predicate 条件分区,默认值为 List | 示例:将用户分为「成年人(≥18)」和「未成年人」:Map<Boolean, List> partitionByAdult = users.stream().collect(Collectors.partitioningBy(u -> u.getAge() >= 18)); |
| partitioningBy(..., Collector<? super T,A,D> downstream) | 重载:分区后二次收集 | .示例:分区后统计每组人数Map<Boolean, Long> adultCount = users.stream().collect(Collectors.partitioningBy(u -> u.getAge() >= 18, Collectors.counting())); |
- 字符串拼接:joining
专门用于将 Stream 中的字符串元素(或可转为字符串的元素)拼接为一个字符串,支持分隔符、前缀、后缀。
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| joining() | 直接拼接(无分隔符) | String join = Stream.of("a", "b", "c").collect(Collectors.joining()); // 结果:"abc" |
| joining(CharSequence delimiter) | 按分隔符拼接 | String join = Stream.of("a", "b", "c").collect(Collectors.joining(",")); // 结果:"a,b,c" |
| joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) | 按分隔符拼接,且添加前缀和后缀 | String join = Stream.of("a", "b", "c").collect(Collectors.joining(",", "[", "]")); // 结果:"[a,b,c]" |
- 规约与收集:reducing
reducing 是通用的聚合方法,支持自定义聚合逻辑(类似 Stream.reduce()),但可作为复合收集器(如分组后聚合)。
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| reducing(T identity, BinaryOperator accumulator) | 以 identity 为初始值,用 accumulator 聚合元素 | 示例:计算年龄总和(等价于 summingInt):int totalAge = users.stream().map(User::getAge).collect(Collectors.reducing(0, Integer::sum)); |
| reducing(BinaryOperator accumulator) | 无初始值,返回 Optional(避免空流报错) | Optional totalAge = users.stream().map(User::getAge).collect(Collectors.reducing(Integer::sum)); |
| reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator++accumulator)++ | 先通过 mapper 转换元素,再聚合 | 示例:转换为年龄后求和:int totalAge = users.stream().collect(Collectors.reducing(0, User::getAge, Integer::sum)); |
- 其他实用方法
| 方法签名 | 功能描述 | 示例 |
|---|---|---|
| mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream) | 先转换元素,再用下游收集器收集(常用于复合场景,如分组后转换) | 示例:分组后收集用户名到 Set:Map<String, Set> genderNames = users.stream().collect(Collectors.groupingBy(User::getGender, Collectors.mapping(User::getName, Collectors.toSet()))); |
| filtering(Predicate<? super T> predicate, Collector<? super T,A,R> downstream) | 先过滤元素,再用下游收集器收集(Java 9+ 新增,Java 8 需手动先 filter() | 示例:过滤成年人后收集为 List:List adults = users.stream().collect(Collectors.filtering(u -> u.getAge() >= 18, Collectors.toList())); |
| collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) | 先通过下游收集器收集,再对结果执行 finisher 转换 | 示例:收集为 List 后转为不可变集合:List immutableList = stream.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); |
核心注意事项(Java 8 版本限制)
1、filtering 是 Java 9+ 方法:Java 8 中需先调用 stream.filter(...) 再收集,不能直接用 Collectors.filtering;
2、toList() 实现不固定:Java 8 中默认返回 ArrayList,但官方未明确约定,若需固定集合类型(如 LinkedList),建议用 toCollection(LinkedList::new);
3、toMap() 键冲突报错:若未指定 mergeFunction,当出现重复 key 时会抛出 IllegalStateException,必须显式处理冲突;
4、并行流支持:所有 Collectors 方法均支持并行流(parallelStream),无需额外处理线程安全(底层通过 Combiner 合并结果)。