用流收集数据Collectors的用法介绍分组groupingBy、分区partitioningBy(一)


这一张我们主要讲用流来收集数据,函数式的编程相对于指令式编程的一个优势:你只需指出希望的结果做什么,而不用操心执行的步骤怎么做。

这一节所讲的方法都是基于收集collect里面参数Collectors里面的静态方法静态方法。

小插曲:import static java.util.stream.Collectors.*;可以把所有静态方法引入

一、收集器简介

收集器非常有用,因为它可以简洁而又灵活地定义collect用来生成结果集合的标准。更具体说,对流调用collect方法将对流中的元素发一个归约操作。
本章剩下部分,主要探讨预定义收集器功能,也就是那些可以从Collector类提供的工厂方法创建收集器。主要提供三大功能:

  • 将流元素归约和汇总一个值
  • 元素分组
  • 元素分区

二、归约和汇总

利用counting

public static void testCounting() {
        Long collect = menu.stream().collect(counting());
    }

1、查找流中最大值和最小值Collectors.maxBy和,Collectors.minBy

maxBy参数接受一个Compartor比较结果。例:

 public static void testMaxby() {
        Comparator<Dish> dishComparator = Comparator.comparingInt(Dish::getCalories);
        Optional<Dish> collect = menu.stream().collect(maxBy(dishComparator));
    }

2、汇总

Collectors类专门为汇总提供了一个工厂方法:Collectors.summingInt。接受一个对象映射为求和所需int函数。例:

public static void testSummingInt() {
        Integer collect = menu.stream().collect(summingInt(Dish::getCalories));
        System.out.println(collect);
    }

Collectors.summingLong和Collectors.summingDouble方法用法完全一样可以用于求和long和double,求平球输方法averagingLong、averagingDouble、averagingInt。
summarizingInt、summarizingLong、summarizingDouble方法可以数出元素个数、平均数、总和、最大值和最小值。例:

IntSummaryStatistics menuStatistics =
menu.stream().collect(summarizingInt(Dish::getCalories));

3、连接字符串

joining工厂方法返回的收集器会把中每一个对象应用toString()方法得到的所有字符串
连接成一个字符串。例:把所有的菜肴名称连接起来

public static void testJoining() {
        String collect = menu.stream().map(Dish::getName).collect(joining());
        System.out.println(collect);
    }

重载方法可以添加用什么分隔符

public static void testJoining() {
        String collect = menu.stream().map(Dish::getName).collect(joining(","));
        System.out.println(collect);
    }

4、广义归约汇总

前面已经讨论过的收集器,可以用一个reduing工厂方法定义归约过程的特殊情况。例:

 public static void testCollectReduing() {
        Integer collect = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));
    }

它需要三个参数:
第一个参数:归约起始位置
第二个参数:转换成表示所函热量的int
第三个参数:是一个BInaryOperator,将两个项目累计为同一个值。例如对int求和。

三、分组

java8分组类似于常见数据库分组,我们java8提供了Collectors.groupingBy工厂方法返回的收集器就可以轻松的完成分组。例:
按照菜单类型分组

Map<Dish.Type, List<Dish>> dishesByType =
menu.stream().collect(groupingBy(Dish::getType));

1、多级分组

实现多级分组,可以使用一个由双参数版本的Collectors.groupingBy工厂方法创建收集器,除了普通的分类函数外,还可以接受collector类型的第二个参数。进行二级分组可以把内层的groupingby传递给外层。
例:

 public static void testCollecGroupBying() {
        Map<Type, Map<CaloricLevel, List<Dish>>> collect = menu.stream().collect(groupingBy(Dish::getType, groupingBy(dish ->
                {
                    if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                    else if (dish.getCalories() <= 700) return CaloricLevel.NOMAL;
                    else return CaloricLevel.FAT;
                }
        )));
        System.out.println(collect);
    }

n级分组就会得到一个n级树形结构的n级Map
在这里插入图片描述

2、按子组收集数据

传递给第一个groupingBy的第二个收集器可以是任何类型,而不一定是一个groupingBy。例:

Map<Dish.Type, Long> typesCount = menu.stream().collect(
groupingBy(Dish::getType, counting()));

注意:普通单参数groupingBy单参数其实就是groupbingBy(f,toList)的简写

在举一个例子加深印象

Map<Type, Optional<Dish>> collect1 = menu.stream()
                .collect(groupingBy(Dish::getType,
                        maxBy(Comparator.comparingInt(Dish::getCalories))));

3、将收集器结果转换为另一种类型

收集器转换成另一种类型可以用:Collectors.collectingAndThen
例:

 public static void testCollectAndThen() {
        Map<Type, Dish> collect = menu.stream().collect(groupingBy(Dish::getType,
                collectingAndThen(maxBy(Comparator.comparingInt(Dish::getCalories)), Optional::get)));
    }
}

collectingAndThen工厂发方法接受两个参数:1、转换收集器(Collector);2、转换函数(Function)。返回值是另一个收集器。

4、分区partitioningBy

分区的好处在于保留分区函数返回true或false的两套流元素列表。partitioningBy工厂方法有一个重载方法,第二个参数可以传递收集器。

public static void testPartitioningBy() {
        Map<Boolean, Map<Type, List<Dish>>> collect = menu.stream().
                collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType)));
    }

总结:Collectors类的静态工厂方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值