Java 下游收集器,用户希望对 groupingBy
或 partitioningBy
操作返回的集合进行后期处理,使用 java.util.stream.Collectors
类定义的某种静态工具方法。有关将元素划分为多个类别的讨论请参见范例分区与分组。
Java 下游收集器 问题描述
用户希望对 groupingBy
或 partitioningBy
操作返回的集合进行后期处理。
Java 下游收集器 解决方案
使用 java.util.stream.Collectors
类定义的某种静态工具方法。
Java 下游收集器 具体实例
有关将元素划分为多个类别的讨论请参见范例分区与分组 。groupingBy
和 partitioningBy
方法返回的是 Map
,其中键为类别(对于 partitioningBy
方法是布尔值 true
或 false
,对于 groupingBy
方法是对象),值为满足各个类别的元素列表。读者或许还记得根据偶数和奇数长度对字符串进行分区的示例(例 4-20),为便于参考,例 4-22 完整复制了这个示例。
例 4-22 根据偶数或奇数长度对字符串分区
List<String> strings = Arrays.asList("this", "is", "a", "long", "list", "of",
"strings", "to", "use", "as", "a", "demo");
Map<Boolean, List<String>> lengthMap = strings.stream()
.collect(Collectors.partitioningBy(s -> s.length() % 2 == 0));
lengthMap.forEach((key,value) -> System.out.printf("%5s: %s%n", key, value));
//
// false: [a, strings, use, a]
// true: [this, is, long, list, of, to, as, demo]
较之实际的列表,我们或许对每个类别包含多少元素更感兴趣。换言之,我们可能只需要各个列表中的元素数量,而不是返回 Map
(值为 List<String>
)。partitioningBy
方法的重载形式如下,其第二个参数为 Collector
类型:
static <T,D,A> Collector<T,?,Map<Boolean,D>> partitioningBy(
Predicate<? super T> predicate, Collector<? super T,A,D> downstream)
静态方法 Collectors.counting
的作用就在于此,其用法如例 4-23 所示。
例 4-23 对已分区的字符串进行计数
Map<Boolean, Long> numberLengthMap = strings.stream()
.collect(Collectors.partitioningBy(s -> s.length() % 2 == 0,
Collectors.counting())); ➊
numberLengthMap.forEach((k,v) -> System.out.printf("%5s: %d%n", k, v));
//
// false: 4
// true: 8
➊ 下游收集器
这就是所谓的下游收集器,它对下游的结果列表(即在分区操作完成之后)进行后期处理。
groupingBy
方法也有一种传入下游收集器的重载形式:
/**
* @param <T>:输入元素的类型
* @param <K>:键的类型
* @param <A>:下游收集器的中间累加类型
* @param <D>:下游归约操作的结果类型
* @param classifier:将输入元素映射到键的分类器函数
* @param downstream:实现下游归约操作的Collector
* @return:实现级联分组操作的Collector
*/
static <T,K,A,D> Collector<T,?,Map<K,D>> groupingBy(
Function<? super T,? extends K> classifier,
Collector<? super T,A,D> downstream)
方法签名中包含了部分 Javadoc 注释,其中 T
为集合中元素的类型,K
为结果映射的键类型,A
为累加器,D
为下游收集器的类型,?
表示“未知”。有关泛型在 Java 8 中的应用,详细信息请参见泛型与 Java 8。
Stream
接口定义的部分方法在 Collectors
类中存在类似的对应,它们的对比如表 4-2 所示。
表4-2:Stream
接口定义的方法与Collectors
类定义的方法
Stream |
Collectors |
---|---|
count |
counting |
map |
mapping |
min |
minBy |
max |
maxBy |
IntStream.sum |
summingInt |
DoubleStream.sum |
summingDouble |
LongStream.sum |
summingLong |
IntStream.summarizing |
summarizingInt |
DoubleStream.summarizing |
summarizingDouble |
LongStream.summarizing |
summarizingLong |
需要再次强调的是,下游收集器用于对上游操作(如分区或分组)产生的对象集合进行后期处理。