Java 中的 Collectors 类

2025 年 5 月 29 日 | 阅读 10 分钟

Collectors 是一个 final 类,它继承自 Object 类。它提供了规约(reduction)操作,例如将元素累积到集合中,根据各种标准对元素进行汇总等。

该类属于 java.util.stream 包。这意味着 Collectors 类是 Java 8 中引入的 Stream API 的一部分,它以函数式编程风格提供了对集合执行聚合操作的功能。

Collectors 类的目的

Collectors 类的主要目的是充当 Collector 实现的工厂,这些实现对于常见的规约操作非常有用。
它允许我们将流转换为各种集合类型(如 List、Set 或 Map),执行分组、分区、汇总,甚至简单的字符串连接。

Java Collectors 类方法

Java Collectors 类提供了各种方法来处理元素。

方法描述
public static <T> Collector<T,?,Double> averagingDouble(ToDoubleFunction<? super T> mapper)它返回一个 Collector,该 Collector 对应用于输入元素的双精度值函数执行算术平均。如果不存在元素,则结果为 0。当您想从对象流中计算平均值(例如平均工资或平均评分)时,此方法非常有用。
public static <T> Collector<T,?,T> reducing(T identity, BinaryOperator<T> op)它返回一个 Collector,该 Collector 使用提供的标识符,在指定的 BinaryOperator 下对输入元素执行规约。标识符值用作起点,然后累积应用规约运算符来组合所有元素。
public static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> op)它返回一个 Collector,该 Collector 在指定的 BinaryOperator 下对输入元素执行规约。结果描述为 Optional<T>。此版本不需要标识符值,并返回一个 Optional,如果流为空,则该 Optional 为空。
public static <T,U> Collector<T,?,U> reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)它返回一个 Collector,该 Collector 在指定的映射函数和 BinaryOperator 下对输入元素执行规约。它是 reducing(Object, BinaryOperator) 的泛化,允许在规约之前对元素进行转换。当您需要转换元素后再进行规约时,它特别有用。
public static <T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)它返回一个 Collector,该 Collector 根据分类函数对输入元素 T 进行“分组”操作,并将结果存储在 Map 中。这是流中基于属性(例如按城市分组人或按部门分组员工)对元素进行分组的最常用方法之一。
public static <T,K,A,D> Collector<T,?,Map<K,D>> groupingBy(Function<? super T,? extends K> classifier, Collector<? Super T,A,D> downstream)它返回一个 Collector,该 Collector 根据分类函数对输入元素 T 进行级联“分组”操作,然后使用指定的下游 Collector 对与给定键关联的值执行规约操作。它支持复杂的多级分组和聚合,例如按类别分组和汇总数量。
public static <T, K, D, A, M extends Map<K, D>> Collector<T,?, M> groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream)它返回一个 Collector,该 Collector 根据分类函数对输入元素 T 进行级联“分组”操作,然后使用指定的下游 Collector 对与给定键关联的值执行规约操作。Collector 生成的 Map 使用提供的工厂函数创建。此方法允许完全控制使用的 Map 类型(例如 LinkedHashMap、TreeMap)以及如何处理分组值。
public static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(Function<? super T,? extends K> classifier)它返回一个并发 Collector,该 Collector 根据分类函数对输入元素 T 进行“分组”操作。它专为并行流处理而设计,并使用 ConcurrentMap 生成线程安全的组。
public static <T,K,A,D> Collector<T,?,ConcurrentMap<K,D>> groupingByConcurrent(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)它返回一个并发 Collector,该 Collector 根据分类函数对输入元素 T 进行级联“分组”操作,然后使用指定的下游 Collector 对与给定键关联的值执行规约操作。
public static <T,K,A,D,M extends ConcurrentMap<K,D>> Collector<T,?,M> groupingByConcurrent(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)它返回一个并发 Collector,该 Collector 根据分类函数对输入元素 T 进行级联“分组”操作,然后使用指定的下游 Collector 对与给定键关联的值执行规约操作。Collector 生成的 ConcurrentMap 使用提供的工厂函数创建。
public static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Predicate<? super T> predicate)它返回一个 Collector,该 Collector 根据 Predicate 对输入元素进行分区,并将它们组织到 Map<Boolean, List<T>> 中。对于返回的 Map,对其类型、可变性、可序列化性或线程安全性没有保证。
public static <T,D,A> Collector<T,?,Map<Boolean,D>> partitioningBy(Predicate<? super T> predicate, Collector<? Super T,A,D> downstream)它返回一个 Collector,该 Collector 根据 Predicate 对输入元素进行分区,根据另一个 Collector 对每个分区中的值进行规约,并将它们组织到 Map<Boolean, D> 中,其中值是下游规约的结果。
public static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)它返回一个 Collector,该 Collector 将输入元素累积到 Map 中,该 Map 的键和值是通过对输入元素应用提供的映射函数生成的。如果遇到重复键,除非提供了合并函数,否则将抛出 IllegalStateException。
public static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)它返回一个 Collector,该 Collector 将输入元素累积到 Map 中,该 Map 的键和值是通过对输入元素应用提供的映射函数生成的。
public static <T,K,U,M extends Map<K,U>> Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)它返回一个 Collector,该 Collector 将输入元素累积到 Map 中,该 Map 的键和值是通过对输入元素应用提供的映射函数生成的。
public static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)它返回一个并发 Collector,该 Collector 将输入元素累积到 ConcurrentMap 中,该 ConcurrentMap 的键和值是通过对输入元素应用提供的映射函数生成的。
public static <T,K,U> Collector<T,?,ConcurrentMap<K,U>> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)它返回一个并发 Collector,该 Collector 将输入元素累积到 ConcurrentMap 中,该 ConcurrentMap 的键和值是通过对输入元素应用提供的映射函数生成的。
public static <T,K,U,M extends ConcurrentMap<K,U>> Collector<T,?,M> toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)它返回一个并发 Collector,该 Collector 将输入元素累积到 ConcurrentMap 中,该 ConcurrentMap 的键和值是通过对输入元素应用提供的映射函数生成的。对于高级并行操作,它具有高度的可定制性。
public static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)它返回一个 Collector,该 Collector 对每个输入元素应用一个 int 生成的映射函数,并返回生成的值的摘要统计信息。
public static <T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)它返回一个 Collector,该 Collector 对每个输入元素应用一个 long 生成的映射函数,并返回生成的值的摘要统计信息。一次性包括计数、总和、最小值、最大值和平均值。
public static <T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)它返回一个 Collector,该 Collector 对每个输入元素应用一个 double 生成的映射函数,并返回生成的值的摘要统计信息。在一次操作中收集平均值、计数、最大值、最小值和总和非常有效。
public static <T> Collector<T,?,List<T>> toList()它返回一个 Collector,该 Collector 将输入元素累积到新的 List 中。这是最常用的 Collectors 之一。
public static <T> Collector<T,?,Set<T>> toSet()它返回一个 Collector,该 Collector 将输入元素累积到 Set 中,并在此过程中删除重复项。
public static <T> Collector<T,?,String> joining()它返回一个 Collector,该 Collector 将输入元素按遇到顺序连接成一个单一的 String。仅适用于 CharSequence 流或 toString() 有意义的情况。
public static <T> Collector<T,?,String> joining(CharSequence delimiter)它返回一个 Collector,该 Collector 使用给定的分隔符连接 CharSequence 元素。对于构建逗号分隔的字符串很有用。
public static <T> Collector<T,?,String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)它通过允许可选的前缀和后缀来扩展 joining(delimiter)。对于构建类似 JSON 或 SQL 的字符串结构很有用。
public static <T> Collector<T,?,Long> counting()它返回一个 Collector,该 Collector 计算输入元素的数量。通常用于简单的统计。

示例

示例 1:将数据作为 List 获取

示例

编译并运行

输出

[25000.0, 30000.0, 28000.0, 28000.0, 90000.0]

说明

我们使用 map() 函数提取每个产品的价格。然后,我们使用 Collectors.toList() 将这些价格收集到 List<Float> 中。

时间复杂度: O(n),其中 n 是列表中产品的数量。每个产品都处理一次。

示例 2:将数据作为 Set 转换

示例

编译并运行

输出

[25000.0, 30000.0, 28000.0, 90000.0]

说明

在这里,我们使用 Collectors.toSet() 而不是 toList() 来删除重复的价格。由于 Set 不允许重复,因此只剩下唯一的价格。

时间复杂度: O(n),用于流式传输和插入到 HashSet。

示例 3:使用 sum() 方法

示例

编译并运行

输出

Sum of prices: 201000.0
Sum of id's: 15

说明

我们使用 Collectors.summingDouble() 来对价格求和,使用 summingInt() 来对 ID 求和。这些是专门的 Collectors,有助于聚合数值数据

时间复杂度: O(n),因为每个元素都被遍历一次以计算总和。

示例 4:获取产品平均价格

示例

编译并运行

输出

Average price is: 40200.0

说明

averagingDouble() Collector 首先对所有元素求和,然后除以计数,从而计算出价格的平均值。

时间复杂度: O(n),因为每个产品都被访问一次以计算总和并计数。

示例 5:计数元素

示例

编译并运行

输出

Total elements: 5

说明

Collectors.counting() 是一个终止 Collector,它只计算流中元素的数量。

时间复杂度: O(n),其中 n 是产品的总数。

使用 Collectors 的优点

  • 常见操作的代码简洁易读。
  • 通过内部迭代和单次计算提高性能。
  • 使用 groupingByConcurrent() 和 toConcurrentMap() 轻松与并行流集成。
  • 高度可定制,用于高级数据处理。

Java Collectors 选择题

1. Java 中的 Collectors 类有哪些有效用途?

  1. 执行数学计算
  2. 对流执行规约操作
  3. 处理流中的异常
  4. 打开 Java 中的文件
 

答案:2)

解释: Collectors 类提供了各种静态方法来执行规约操作,例如 toList()、groupingBy() 和 averagingDouble()。


2. Collectors.toList() 方法返回什么?

  1. 一个数组
  2. 一个流
  3. 包含流元素的 List
  4. 包含流元素的 Set
 

答案:3)

解释: Collectors.toList() 将流的元素收集到一个 List 中。


3. 哪个 Collectors 方法用于按分类函数对元素进行分组?

  1. toMap()
  2. groupingBy()
  3. partitioningBy()
  4. averagingDouble()
 

答案:2)

解释: Collectors.groupingBy() 用于按分类函数对流元素进行分组并将它们收集到 Map 中。


4. Collectors.averagingDouble() 返回什么?

  1. 一个双精度值流
  2. 一个整数平均值
  3. 一个计算双精度值平均值的 Collector
  4. 一个双精度平均值的 Map
 

答案:3)

解释:此方法返回一个 Collector,该 Collector 计算流元素的双精度值的平均值。


5. partitioningBy() 返回什么?

  1. 一个分区列表
  2. 一个 Map<Boolean, List<T>>
  3. 一个 Map<Integer, List<T>>
  4. 一个单一的布尔结果
 

答案:2)

解释: Collectors.partitioningBy() 根据 Predicate 将元素分成两组,并将它们作为 Map<Boolean, List<T>> 返回。