Java Stream API进阶篇

发表于:2017-3-15 10:24

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:CarpenterLee    来源:51Testing软件测试网采编

分享:
  使用collect()生成Map
  前面已经说过 Stream 背后依赖于某种数据源,数据源可以是数组、容器等,但不能是 Map 。反过来从 Stream 生成 Map 是可以的,但我们要想清楚 Map 的 key 和 value 分别代表什么,根本原因是我们要想清楚要干什么。通常在三种情况下 collect() 的结果会是 Map :
  使用 Collectors.toMap() 生成的收集器,用户需要指定如何生成 Map 的 key 和 value 。
  使用 Collectors.partitioningBy() 生成的收集器,对元素进行二分区操作时用到。
  使用 Collectors.groupingBy() 生成的收集器,对元素做 group 操作时用到。
  情况1:使用 toMap() 生成的收集器,这种情况是最直接的,前面例子中已提到,这是和 Collectors.toCollection() 并列的方法。如下代码展示将学生列表转换成由 <学生 gpa=""> 组成的 Map 。非常直观,无需多言。
  // 使用toMap()统计学生GPA
  Map<Student, Double> studentToGPA =
  students.stream().collect(Collectors.toMap(Functions.identity(),// 如何生成key
  student -> computeGPA(student)));// 如何生成value
  情况2:使用 partitioningBy() 生成的收集器,这种情况适用于将 Stream 中的元素依据某个二值逻辑(满足条件,或不满足)分成互补相交的两部分,比如男女性别、成绩及格与否等。下列代码展示将学生分成成绩及格或不及格的两部分。
  // Partition students into passing and failing
  Map<Boolean, List<Student>> passingFailing = students.stream()
  .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
  情况3:使用 groupingBy() 生成的收集器,这是比较灵活的一种情况。跟SQL中的 group by 语句类似,这里的 groupingBy() 也是按照某个属性对数据进行分组,属性相同的元素会被对应到 Map 的同一个 key 上。下列代码展示将员工按照部门进行分组:
  // Group employees by department
  Map<Department, List<Employee>> byDept = employees.stream()
  .collect(Collectors.groupingBy(Employee::getDepartment));
  以上只是分组的最基本用法,有些时候仅仅分组是不够的。在SQL中使用 group by 是为了协助其他查询,比如 1. 先将员工按照部门分组,2. 然后统计每个部门员工的人数 。Java类库设计者也考虑到了这种情况,增强版的 groupingBy() 能够满足这种需求。增强版的 groupingBy() 允许我们对元素分组之后再执行某种运算,比如求和、计数、平均值、类型转换等。这种先将元素分组的收集器叫做 上游收集器 ,之后执行其他运算的收集器叫做 下游收集器 ( downstream Collector )。
  // 使用下游收集器统计每个部门的人数
  Map<Department, Integer> totalByDept = employees.stream()
  .collect(Collectors.groupingBy(Employee::getDepartment,
  Collectors.counting()));// 下游收集器
  上面代码的逻辑是不是越看越像SQL?高度非结构化。还有更狠的,下游收集器还可以包含更下游的收集器,这绝不是为了炫技而增加的把戏,而是实际场景需要。考虑将员工按照部门分组的场景,如果 我们想得到每个员工的名字(字符串),而不是一个个 Employee 对象 ,可通过如下方式做到:
  // 按照部门对员工分布组,并只保留员工的名字
  Map<Department, List<String>> byDept = employees.stream()
  .collect(Collectors.groupingBy(Employee::getDepartment,
  Collectors.mapping(Employee::getName,// 下游收集器
  Collectors.toList())));// 更下游的收集器
  如果看到这里你还没有对Java函数式编程失去信心,恭喜你,你已经顺利成为Java函数式编程大师了。
  使用collect()做字符串join
  这个肯定是大家喜闻乐见的功能,字符串拼接时使用 Collectors.joining() 生成的收集器,从此告别 for 循环。 Collectors.joining() 方法有三种重写形式,分别对应三种不同的拼接方式。无需多言,代码过目难忘。
  // 使用Collectors.joining()拼接字符串
  Stream<String> stream = Stream.of("I", "love", "you");
  //String joined = stream.collect(Collectors.joining());// "Iloveyou"
  //String joined = stream.collect(Collectors.joining(","));// "I,love,you"
  String joined = stream.collect(Collectors.joining(",", "{", "}"));// "{I,love,you}"
  collect()还可以做更多
  除了可以使用 Collectors 工具类已经封装好的收集器,我们还可以自定义收集器,或者直接调用 collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner) 方法, 收集任何形式你想要的信息 。不过 Collectors 工具类应该能满足我们的绝大部分需求,手动实现之间请先看看文档。
22/2<12
重磅发布,2022软件测试行业现状调查报告~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2023
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号