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


    用流收集数据

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

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

    1. public void printlnTest(){
    2. /**
    3. * 函数式编程相对于指令式编程的一个主要优势:你只需指出希望的
    4. * 结果——“做什么”,而不用操心执行的步骤——“如何做”。
    5. */
    6. ArrayList userList = Lists.newArrayList();
    7. userList.add(User.builder().sex("女").name("小红").build());
    8. userList.add(User.builder().sex("女").name("小花").build());
    9. userList.add(User.builder().sex("男").name("小张").build());
    10. userList.add(User.builder().sex("男").name("小网").build());
    11. userList.add(User.builder().sex("男").name("小里").build());
    12. // 使用 groupingBy() 按男女性别分组
    13. Map> sexGroup = userList.stream().collect(groupingBy(User::getSex));
    14. // 打印: sexGroup ==>
    15. // {女=[User(name=小红, sex=女, ...),User(name=小花, sex=女, ...)],
    16. // 男=[User(name=小张,sex=男, ...),User(name=小网, sex=男, ...),User(name=小里, sex=男, ...)]}
    17. System.out.println("sexGroup ==> " + sexGroup);
    18. // 统计分组的个数
    19. Map groupCount = userList.stream().collect(groupingBy(User::getSex, counting()));
    20. // groupCount ==> {女=2, 男=3}
    21. System.out.println("groupCount ==> " + groupCount.toString());
    22. /**
    23. * Collectors实用类提供了很多静态工厂方法,
    24. * 可以方便地创建常见收集器的实例,只要拿来用就可以了。最直接和最常用的收集器是toList
    25. * 静态方法,它会把流中所有的元素收集到一个List中:
    26. */
    27. List users = userList.stream().collect(Collectors.toList());
    28. /**
    29. * Collectors
    30. * 类提供的工厂方法(例如groupingBy)创建的收集器。它们主要提供了三大功能:
    31. * 将流元素归约和汇总为一个值
    32. * 元素分组
    33. * 元素分区
    34. * 我们先来看看可以进行归约和汇总的收集器。
    35. */
    36. // 利用counting工厂方法返回的收集器,看有几个人
    37. // counting收集器在和其他收集器联合使用的时候特别有用,后面会谈到这一点
    38. long peopleCount = userList.stream().collect(Collectors.counting());
    39. // 或者 userList.stream().count(); userList.size();
    40. // Java Stream Collectors的maxBy() minBy()、groupingBy()、partitioningBy()的使用
    41. // userList.stream().map(user -> user.getUId()).collect(Collectors.maxBy(Comparator.naturalOrder()));
    42. Optional comparatorMax = userList.stream().map(User::getUId).max(Comparator.naturalOrder());
    43. System.out.println("stream() comparatorMax :" + comparatorMax.orElse(0));
    44. // minBy()
    45. Optional comparatorMin = userList.stream().map(User::getUId).min(Comparator.naturalOrder());
    46. System.out.println("stream() comparatorMin :" + comparatorMax.orElse(0));
    47. // summingInt 求和
    48. // userList.stream().collect(summingInt(User::getUId))
    49. int summingInt = userList.stream().mapToInt(User::getUId).sum();
    50. }

    groupingBy的理解图:

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

    使用partitioningBy、collectingAndThen

    1. public void demo(){
    2. ArrayList userList = Lists.newArrayList();
    3. userList.add(User.builder().sex("女").name("小红").type(true).uId(10).build());
    4. userList.add(User.builder().sex("女").name("小花").type(false).uId(11).build());
    5. userList.add(User.builder().sex("男").name("小张").type(true).uId(12).build());
    6. userList.add(User.builder().sex("男").name("小网").type(false).uId(13).build());
    7. userList.add(User.builder().sex("男").name("小里").type(true).uId(14).build());
    8. /**
    9. * 先根据类型分组、再根据性别分组
    10. */
    11. Map>> collect = userList.stream().collect(partitioningBy(User::isType, groupingBy(User::getSex)));
    12. // {false={
    13. // 女=[
    14. // User(uId=null, name=小花, email=null, sex=女, type=false)
    15. // ],
    16. // 男=[
    17. // User(uId=null, name=小网, email=null, sex=男, type=false)
    18. // ]},
    19. //
    20. // true={
    21. // 女=[
    22. // User(uId=null, name=小红, email=null, sex=女, type=true)
    23. // ],
    24. // 男=[
    25. // User(uId=null, name=小张, email=null, sex=男, type=true),
    26. // User(uId=null, name=小里, email=null, sex=男, type=true)
    27. // ]}
    28. // }
    29. System.out.println(collect.toString());
    30. // 查询true和false中两个类型中,uid最大的对象
    31. Map collectType = userList.stream().collect(partitioningBy(User::isType, collectingAndThen(maxBy(comparingInt(User::getUId)), Optional::get)));
    32. // {
    33. // false = User(uId=13, name=小网, sex=男, type=false),
    34. // true = User(uId=14, name=小里, sex=男, type=true)
    35. // }
    36. System.out.println(collectType.toString());
    37. }

    Collector收集器的介绍及使用

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

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

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

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


    第六章结尾:开发你自己的收集器以获得更好的性能【略】
  • 相关阅读:
    详述Java内存屏障,透彻理解volatile
    jsonXML格式化核心代码
    STM32——NVIC中断优先级管理分析
    Meta Llama 3 里面装饰器
    大数据库练习题
    Delphi绘图功能[1] —— 入门(绘制直线和矩形)
    Bootstrap Your Own Latent: A New Approach to Self-Supervised Learning
    通用与垂直大模型:应用、挑战与未来发展
    LeetCode 1779. 找到最近的有相同 X 或 Y 坐标的点
    Kotlin 协程 (6/6篇) - 跨协程间通信 Channel
  • 原文地址:https://blog.csdn.net/amosjob/article/details/126453071