带你了解 Java 8 Stream:掌握流处理中的收集器技巧

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

什么是收集器(Collectors)

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

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

常见的收集器用法

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

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

通过 counting 统计集合总数

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

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

Optional<Student> optional = studentList.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge));)

if (optional.isPresent()){
    Student student = optional.get();
    System.out.println(student);
}

通过 summingLnt 进行数据汇总

Integer collect  = peopleStream.collect(Collectors.summingLnt(Person::getAge));

int sum = peopleStream.mapToInt(Person::getAge).sum();

通过 averagingLnt 进行平均值获取

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

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

通过 joining 进行数据拼接

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

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

通过 groupingBy 将数据进行分组

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()。

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

  • 定义自定义收集器
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);
  }
}
  • 使用自定义收集器
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 不可或缺的一部分。在处理流数据时,收集器可以帮助我们更轻松地完成数据的汇总、分组、分区等操作。通过本文的介绍,相信你已经对收集器有了更深入的了解,并能够在实际项目中灵活运用收集器的各种技巧。让我们在流处理的旅程中更加游刃有余!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值