Java8实战[第6章]用流收集数据groupingBy、partitioningBy、collectingAndThen、max、min、sum

用流收集数据

  1. 用流收集数据 、
  2. 用Collectors类创建和使用收集器
  3. 将数据流归约为一个值
  4. 汇总:归约的特殊情况
  5. 数据分组和分区
  6. 开发自己的自定义收集器

使用groupingBy分组、以及max、min、sum

    public void printlnTest(){
        /**
         * 函数式编程相对于指令式编程的一个主要优势:你只需指出希望的
         * 结果——“做什么”,而不用操心执行的步骤——“如何做”。
         */
        ArrayList<User> userList = Lists.newArrayList();
        userList.add(User.builder().sex("女").name("小红").build());
        userList.add(User.builder().sex("女").name("小花").build());
        userList.add(User.builder().sex("男").name("小张").build());
        userList.add(User.builder().sex("男").name("小网").build());
        userList.add(User.builder().sex("男").name("小里").build());

        // 使用 groupingBy() 按男女性别分组
        Map<String, List<User>> sexGroup = userList.stream().collect(groupingBy(User::getSex));
        //  打印: sexGroup ==>
        //  {女=[User(name=小红, sex=女, ...),User(name=小花, sex=女, ...)],
        //  男=[User(name=小张,sex=男, ...),User(name=小网, sex=男, ...),User(name=小里, sex=男, ...)]}
        System.out.println("sexGroup ==> " + sexGroup);

        // 统计分组的个数
        Map<String, Long> groupCount = userList.stream().collect(groupingBy(User::getSex, counting()));
        // groupCount ==> {女=2, 男=3}
        System.out.println("groupCount ==> " + groupCount.toString());

        /**
         * Collectors实用类提供了很多静态工厂方法,
         *   可以方便地创建常见收集器的实例,只要拿来用就可以了。最直接和最常用的收集器是toList
         *   静态方法,它会把流中所有的元素收集到一个List中:
         */
        List<User> users = userList.stream().collect(Collectors.toList());

        /**
         * Collectors
         * 类提供的工厂方法(例如groupingBy)创建的收集器。它们主要提供了三大功能:
         *  将流元素归约和汇总为一个值
         *  元素分组
         *  元素分区
         * 我们先来看看可以进行归约和汇总的收集器。
         */
        // 利用counting工厂方法返回的收集器,看有几个人
        // counting收集器在和其他收集器联合使用的时候特别有用,后面会谈到这一点
        long peopleCount = userList.stream().collect(Collectors.counting());
        // 或者 userList.stream().count();  userList.size();

        // Java Stream Collectors的maxBy() minBy()、groupingBy()、partitioningBy()的使用
        // userList.stream().map(user -> user.getUId()).collect(Collectors.maxBy(Comparator.naturalOrder()));
        Optional<Integer> comparatorMax = userList.stream().map(User::getUId).max(Comparator.naturalOrder());
        System.out.println("stream() comparatorMax :" + comparatorMax.orElse(0));

        // minBy()
        Optional<Integer> comparatorMin = userList.stream().map(User::getUId).min(Comparator.naturalOrder());
        System.out.println("stream() comparatorMin :" + comparatorMax.orElse(0));

        // summingInt 求和
        // userList.stream().collect(summingInt(User::getUId))
        int summingInt = userList.stream().mapToInt(User::getUId).sum();
    }

groupingBy的理解图:

groupingBy联合使用的其他收集器的例子,一般来说,通过groupingBy工厂方法的第二个参数传递的收集器将会对分到同一组中的所 有流元素执行进一步归约操作。例如,你还重用求出所有菜肴热量总和的收集器,不过这次是对 每一组Dish求和:
Map<Dish.Type, Integer> totalCaloriesByType = menu.stream()
    .collect(groupingBy(Dish::getType,summingInt(Dish::getCalories)));
然而常常和groupingBy联合使用的另一个收集器是mapping方法生成的。这个方法接受两个参数:一个函数对流中的元素做变换,另一个则将变换的结果对象收集起来。其目的是在累加 之前对每个输入元素应用一个映射函数,这样就可以让接受特定类型元素的收集器适应不同类型 的对象。我们来看一个使用这个收集器的实际例子。比方说你想要知道,对于每种类型的Dish, 菜单中都有哪些CaloricLevel。我们可以把groupingBy和mapping收集器结合起来,如下所示:
Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
      menu.stream().collect(groupingBy(Dish::getType, 
                mapping(dish -> { if (dish.getCalories() <= 400) 
          return CaloricLevel.DIET;
          else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
          else return CaloricLevel.FAT; },
          toSet() )));

使用partitioningBy、collectingAndThen

    public void demo(){
        ArrayList<User> userList = Lists.newArrayList();
        userList.add(User.builder().sex("女").name("小红").type(true).uId(10).build());
        userList.add(User.builder().sex("女").name("小花").type(false).uId(11).build());
        userList.add(User.builder().sex("男").name("小张").type(true).uId(12).build());
        userList.add(User.builder().sex("男").name("小网").type(false).uId(13).build());
        userList.add(User.builder().sex("男").name("小里").type(true).uId(14).build());

        /**
         * 先根据类型分组、再根据性别分组
         */
        Map<Boolean, Map<String, List<User>>> collect = userList.stream().collect(partitioningBy(User::isType, groupingBy(User::getSex)));
        // {false={
        //         女=[
        //         User(uId=null, name=小花, email=null, sex=女, type=false)
        //         ],
        //         男=[
        //         User(uId=null, name=小网, email=null, sex=男, type=false)
        //         ]},
        //
        //   true={
        //         女=[
        //         User(uId=null, name=小红, email=null, sex=女, type=true)
        //         ],
        //         男=[
        //         User(uId=null, name=小张, email=null, sex=男, type=true),
        //         User(uId=null, name=小里, email=null, sex=男, type=true)
        //         ]}
        // }
        System.out.println(collect.toString());

        // 查询true和false中两个类型中,uid最大的对象
        Map<Boolean, User> collectType = userList.stream().collect(partitioningBy(User::isType, collectingAndThen(maxBy(comparingInt(User::getUId)), Optional::get)));
        // {
        //    false = User(uId=13, name=小网, sex=男, type=false),
        //    true  = User(uId=14, name=小里, sex=男, type=true)
        // }
        System.out.println(collectType.toString());

    }

Collector收集器的介绍及使用

一个Collector收集器由四个函数指定,这四个函数一起工作以将条目累积到可变结果容器中,并可选择对结果执行最终转换。他们是:

  • 创建新的结果容器(supplier())
  • 将新数据元素合并到结果容器中(accumulator())
  • 将两个结果容器合并为一个(combiner())
  • 在容器上执行可选的最终转换(finisher())

收集器还具有一组特征,例如Collector.Characteristics.CONCURRENT,提供了可以缩减实现使用,以提供更好性能的提示。

使用收集器缩减的顺序实现将使用supplier函数创建单个结果容器,并为每个输入元素调用一次accumulator累加器函数。并行实现将对输入进行分区,为每个分区创建一个结果容器,将每个分区的内容累积到该分区的子结果中,然后使用combiner组合器函数将子结果合并为组合结果。为确保顺序和并行执行产生相同的结果,收集器函数必须满足标识和关联约束。


第六章结尾:开发你自己的收集器以获得更好的性能【略】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值