4.6 下游收集器
问题
用户希望对 groupingBy 或 partitioningBy 操作返回的集合进行后期处理。
方案
使用 java.util.stream.Collectors 类定义的某种静态工具方法。
讨论
有关将元素划分为多个类别的讨论请参见范例 4.5。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)。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 中的应用,详细信息请参见附录 A。
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
|
需要再次强调的是,下游收集器用于对上游操作(如分区或分组)产生的对象集合进行后期处理。
另见
有关应用下游收集器确定词典中最长单词的讨论请参见范例 7.1,有关 partitioningBy 和 groupingBy 方法的深入讨论请参见范例 4.5,有关泛型的详细信息请参见附录 A。
