如何按子组收集数据?
Posted jiengd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何按子组收集数据?相关的知识,希望对你有一定的参考价值。
例如,要数一数菜单中每类菜有多少个,可以传递counting收集器作为
groupingBy收集器的第二个参数:
Map<Dish.Type, Long> typesCount = menu.stream().collect(
groupingBy(Dish::getType, counting()));
其结果是下面的Map:
{MEAT=3, FISH=2, OTHER=4}
还要注意,普通的单参数groupingBy(f)(其中f是分类函数)实际上是groupingBy(f,
toList())的简便写法。
再举一个例子,你可以把前面用于查找菜单中热量最高的菜肴的收集器改一改,按照菜的类
型分类:
Map<Dish.Type, Optional<Dish>> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
maxBy(comparingInt(Dish::getCalories))));
这个分组的结果显然是一个map,以Dish的类型作为键,以包装了该类型中热量最高的Dish
的Optional<Dish>作为值:
{FISH=Optional[salmon], OTHER=Optional[pizza], MEAT=Optional[pork]}
注意 这个Map中的值是Optional,因为这是maxBy工厂方法生成的收集器的类型,但实际上,
如果菜单中没有某一类型的Dish,这个类型就不会对应一个Optional. empty()值,
而且根本不会出现在Map的键中。groupingBy收集器只有在应用分组条件后,第一次在
流中找到某个键对应的元素时才会把键加入分组Map中。这意味着Optional包装器在这
里不是很有用,因为它不会仅仅因为它是归约收集器的返回类型而表达一个最终可能不
存在却意外存在的值。
1. 把收集器的结果转换为另一种类型
因为分组操作的Map结果中的每个值上包装的Optional没什么用,所以你可能想要把它们
去掉。要做到这一点,或者更一般地来说,把收集器返回的结果转换为另一种类型,你可以使用
Collectors.collectingAndThen工厂方法返回的收集器,如下所示。
代码清单6-3 查找每个子组中热量最高的Dish
Map<Dish.Type, Dish> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));
这个工厂方法接受两个参数——要转换的收集器以及转换函数,并返回另一个收集器。这个
收集器相当于旧收集器的一个包装,collect操作的最后一步就是将返回值用转换函数做一个映
射。在这里,被包起来的收集器就是用maxBy建立的那个,而转换函数Optional::get则把返
回的Optional中的值提取出来。前面已经说过,这个操作放在这里是安全的,因为reducing
收集器永远都不会返回Optional.empty()。
以上是关于如何按子组收集数据?的主要内容,如果未能解决你的问题,请参考以下文章