一文带你了解Java8 Stream流处理中的收集器技巧

发布时间 2024-01-12 09:12:50作者: 全琪俊
Java 8 引入的 Stream 极大地简化了集合数据的处理,提供了一种现代、函数式的方式来处理数据,本文将深入探讨 Java 8 Stream 中的收集器,希望对大家有所帮助
 

Java 8 引入的 Stream 极大地简化了集合数据的处理,提供了一种现代、函数式的方式来处理数据。然而,在处理流时,我们经常需要将流的结果汇总到集合中或者进行各种统计计算。这就是收集器(Collectors)发挥作用的地方。本文将深入探讨 Java 8 Stream 中的收集器,介绍收集器的各种用法和技巧,帮助你更好地利用收集器处理数据。

什么是收集器(Collectors)

收集器是 Stream 提供的一个重要功能,用于将流的元素收集到一个结果容器中。通过使用收集器,可以让代码更加方便的进行简化与重用。其内部主要核心是通过 Collectos 完成更加复杂的计算转换从而获取到最终结果。

Java 8 在 Collectors 类中预定义了多个用于收集的方法,使得我们可以轻松地对流的元素进行汇总、分组、分区以及其他各种操作。

常见的收集器用法

通过 toList 将元素收集到集合中

1
2
List<String> names = peopleStream.map(Person::getName)
                                 .collect(Collectors.toList());

通过 counting 统计集合总数

1
Long collect = studentList.stream().collect(Collectors.counting());

通过 maxBy 和 minBy 获取最大值最小值

1
2
3
4
5
Optional<Student> optional = studentList.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge));)
if (optional.isPresent()){
    Student student = optional.get();
    System.out.println(student);
}

通过 summingLnt 进行数据汇总

1
2
Integer collect  = peopleStream.collect(Collectors.summingLnt(Person::getAge));
int sum = peopleStream.mapToInt(Person::getAge).sum();

通过 averagingLnt 进行平均值获取

1
Double collect  = peopleStream.collect(Collectors.averagingLnt(Person::getAge));

其内部是在收集过程中,对所有年龄进行累加,最后除以平均值

通过 joining 进行数据拼接

1
Person collect  = peopleStream.collect(Collectors.joining());

这种方式相当于将流中每一个元素的name属性获取映射,内部通过StringBuilder来把每一个映射的值进行拼接。

通过 groupingBy 将数据进行分组

1
Map<Integer,List<Student>> map = peopleStream.collect(Collectors.groupingBy(Person::getAge));

自定义收集器

已经通过Collectors中提供的静态方法,完成了诸多的收集器操作,虽然它本身提供了诸多方法,但是不一定能够覆盖日常开发中的所有需求,因此,有时还需要我们根据自身的业务去自定义收集器的实现。通过实现 Collector 接口,我们可以完全掌控收集的过程。

源码分析

 

根据源码,Collector 接口需要三个参数。

  • T: 流中要收集的元素类型
  • A: 累加器的类型
  • R:收集的结果类型

如果想要自定义收集器,需要实现 Collector 接口中的五个方法 Supplier、Accumulator、finisher、combiner、characteristics

 

supplier:用于创建一个容器,在调用它时,需要创建一个空的累加器实例,供后续方法使用。

accumulator:基于supplier中创建的累加容器,进行累加操作。

finisher:当遍历完流后,在其内部完成最终转换,返回一个最终结果。

combiner:用于在并发情况下,将每个线程的容器进行合并。

characteristics:用于定义收集器行为,如是否可以并行或使用哪些优化。其本身是一个枚举,内部有三个值,分别为:

  • CONCURRENT:表明收集器是并行的且只会存在一个中间容器。
  • UNORDERED:表明结果不受流中顺序影响,收集是无序的。
  • IDENTITY_FINISH:表明累积器的结果将会直接作为归约的最终结果,跳过finisher()。

实战示例:收集合格的学生

定义自定义收集器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.jdk8.features.stream.collector;
import com.jdk8.features.lambda.Student;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
public class MyCollector implements Collector<Student, List<Student>,List<Student>> {
  @Override
  public Supplier<List<Student>> supplier() {
    return ArrayList::new;
  }
  @Override
  public BiConsumer<List<Student>, Student> accumulator() {
    return ((studentList, student) -> {
      if (student.getIsPass()){
        studentList.add(student);
      }
    });
  }
  @Override
  public BinaryOperator<List<Student>> combiner() {
    return null;
  }
  @Override
  public Function<List<Student>, List<Student>> finisher() {
    return Function.identity();
  }
  @Override
  public Set<Characteristics> characteristics() {
    return EnumSet.of(Characteristics.IDENTITY_FINISH,Characteristics.UNORDERED);
  }
}

使用自定义收集器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.jdk8.features.lambda.Student;
import java.util.ArrayList;
import java.util.List;
public class MyCollectorDemo {
  public static void main(String[] args) {
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student(1,"张三",18,19));
    studentList.add(new Student(2,"李四",19,18));
    studentList.add(new Student(3,"王五",20,21));
    studentList.add(new Student(4,"赵六",21,80));
    List<Student> list = studentList.stream().collect(new MyCollector());
    System.out.println(list);
  }
}

结语

掌握收集器的使用是精通 Java 8 Stream 不可或缺的一部分。在处理流数据时,收集器可以帮助我们更轻松地完成数据的汇总、分组、分区等操作。通过本文的介绍,相信你已经对收集器有了更深入的了解,并能够在实际项目中灵活运用收集器的各种技巧。让我们在流处理的旅程中更加游刃有余!

到此这篇关于一文带你了解Java8 Stream流处理中的收集器技巧的文章就介绍到这了,更多相关Java8 Stream内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!