学会-Java8 Stream Collectors

216 阅读4分钟

Collectors

Collector接口中方法的实现决定了如何对流执行归约操作。但Collectors实用类提供了很多静态工厂方法,可以方便地创建常见收集器的实例,只要拿来用就可以了。

归约操作

  • Collectors.counting

    // 集合个数
    Long collect = employees.stream().collect(Collectors.counting());
    System.out.println(collect);
    System.out.println(employees.stream().count());
    
  • Collectors.maxBy 和 Collectors.minBy

    //最大最小值
    Optional<Employeecollect1 = employees.stream()
      .collect(Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)));
    System.out.println(collect1.get());
    Optional<Employeecollect2 = employees.stream()
      .collect(Collectors.minBy(Comparator.comparingDouble(Employee::getSalary)));
    System.out.println(collect2.get());
    
  • Collectors.summingInt

    // 汇总
      Integer collect3 = employees.stream().collect(Collectors.summingInt(Employee::getId));
      System.out.println(collect3);
    

    Collectors.summingLong和Collectors.summingDouble方法的作用完全一样,可以用于求和字段为long或double的情况。

    Collectors.averagingInt,连同对应的averagingLong和averagingDouble可以计算数值的平均数;

    summarizingInt,summarizingLong和summarizingDouble可以计算总和、平均值、最大值和最小值;得到IntSummaryStatistics,LongSummaryStatistics和DoubleSummaryStatistics

  • Collectors.joining

    joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。

    // 2个重载版本
    List<Employee> employees = EmployeeData.getEmployees();
    String collect = employees.stream().map(Employee::getName)
      .collect(Collectors.joining());
    System.out.println(collect);
    // 添加分隔符
    String collect1 = employees.stream().map(Employee::getName).collect(Collectors.joining(","));
    System.out.println(collect1);
    // 添加分隔符,开头 结尾 
    String collect2 = employees.stream().map(Employee::getName)
      .collect(Collectors.joining(",", "开头", "结尾"));
    System.out.println(collect2);
    
  • Collectors.reducing

    需要三个参数:

    1.第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值和而言0是一个合适的值。

    2.转换函数

    3.是一个BinaryOperator,将两个项目累积成一个同类型的值

    List<Employee> employees = EmployeeData.getEmployees();
      Integer collect = employees.stream().collect(Collectors.reducing(0, Employee::getId, (a, b) -> a + b));
      System.out.println(collect);
    Optional<Employee> collect3 = employees.stream()
      .collect(Collectors.reducing((a, b) -> a.getSalary() > b.getSalary() ? a : b));
    System.out.println(collect3);
    
    Optional<Integer> collect1 = Stream.of(12345).collect(Collectors.reducing((a, b) -> a + b));
    System.out.println(collect1.get());
    Integer collect2 = Stream.of(12345).collect(Collectors.reducing(10, (a, b) -> a + b));
    System.out.println(collect2);
    

分组

Collectors.groupingBy。

List<Employee> employees = EmployeeData.getEmployees();
// 按工资分组
Map<Double, List<Employee>> collect = employees.stream()
  .collect(Collectors.groupingBy(Employee::getSalary));
System.out.println(collect);
Map<String, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingBy(employee -> {
            if (employee.getSalary() > 9000) {
                return "gao";
            } else if (employee.getSalary() > 500) {
                return "zhong";
            } else {
                return "di";
            }
        }));
System.out.println(collect1);
操作分组的元素

Collectors类重载了工厂方法groupingBy,除了常见的分类函数,它的第二变量也接受一个Collector类型的参数。

Map<DoubleList<String>> collect2 = employees.stream()
.collect(Collectors.groupingBy(Employee::getSalary, Collectors.mapping(Employee::getName, Collectors.toList())));
System.out.println(collect2);

Collectors类通过mapping方法提供了另一个Collector函数,它接受一个映射函数和另一个Collector函数作为参数。作为参数的Collector会收集对每个元素执行该映射函数的运行结果。

有三个参数重载版本,多增加一个map生成工厂

 HashMap<DoubleList<Integer>> collect3 = employees.stream()
   .collect(Collectors.groupingBy(Employee::getSalary, HashMap::new, Collectors
                                  .mapping(Employee::getAge, Collectors.toList())));
多级分组

要实现多级分组,可以使用一个由双参数版本的Collectors.groupingBy工厂方法创建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。那么要进行二级分组的话,可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流中项目分类的二级标准。

 // 多级分组
Map<DoubleMap<IntegerList<Employee>>> collect4 = employees.stream().collect
(Collectors.groupingBy(Employee::getSalary,Collectors.groupingBy(Employee::getAge)));
按子组收集数据
  • 把收集器的结果转换为另一种类型

    Map<DoubleOptional<Employee>> collect5 = employees.stream().
       collect(Collectors.groupingBy(Employee::getSalary, Collectors.maxBy(Comparator.comparing(Employee::getName))));
    
    // 把收集器的结果转换为另一种类型
    Map<DoubleEmployeecollect6 = employees.stream().
      collect(Collectors.groupingBy(Employee::getSalary, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Employee::getId)), Optional::get)));   
    
  • 与groupingBy联合使用的其他收集器

     Map<DoubleSet<String>> collect7 = employees.stream().collect(
       Collectors.groupingBy(Employee::getSalary, Collectors.mapping(Employee::getName, Collectors.toSet()))); 
    

    联合使用其他的收集器,可以产生各种类型数据。继续摸索使用中

分区

分区函数返回一个布尔值,这意味着得到的分组Map的键类型是Boolean,于是它最多可以分为两组——true是一组,false是一组。

 List<Employee> employees = EmployeeData.getEmployees();
 Map<Boolean, List<Employee>> collect = employees.stream()
    .collect(Collectors.partitioningBy(employee -> employee.getSalary() > 1000));

总结

image-1 image-2