Java 流处理之收集器详解

yizhihongxing

Java 流处理之收集器详解

Java 8 引入了一个新的 Stream API,其中的收集器(Collector)是 Java 8 可以处理流(Stream)中数据的一个关键工具。收集器是指将流中元素转换成不同形式的操作。在本文中,我们将详细介绍 Java 中的收集器。

收集器的基本概念

Java 8 提供了 22 个预定义的收集器。这些收集器和终止操作结合使用来对流进行收集。Java 中的收集器具有以下基本特征:

  • 可转换为 List、Set 或 Map 等集合数据结构;
  • 可以限制结果集的长度;
  • 可以通过分组、分区等方式对结果进行分组;
  • 可以自定义将流中的元素聚合成一个集合的方式。

收集器分类

Java 中的收集器主要分为以下几类:

1. 转换收集器

转换收集器可以将流中的元素转换为指定类型的集合。Java 提供的转换收集器有以下几种:

  • toList():将流转换为 List 集合;
  • toSet():将流转换为 Set 集合;
  • toMap():将流转换为 Map,Key 和 Value 由 map 接收的两个函数推断出来。

以下是 toList() 和 toSet() 的示例代码:

List<String> list = Arrays.asList("Java", "Python", "C++");
List<String> result = list.stream().collect(Collectors.toList());
System.out.println(result); // [Java, Python, C++]

Set<String> set = Arrays.asList("Java", "Python", "C++", "Java").stream().collect(Collectors.toSet());
System.out.println(set); // [Java, Python, C++]

2. 分组收集器

分组收集器可以将流中的元素按条件分组。Java 提供的分组收集器有以下几种:

  • groupingBy():按照分组条件进行分组;
  • partitioningBy():按照分区条件进行分区。

以下是 groupingBy() 的示例代码:

class Product {
    private String name;
    private String category;
    // 省略 getters 和 setters 方法
}

List<Product> productList = Arrays.asList(
    new Product("A", "Category1"), new Product("B", "Category2"),
    new Product("C", "Category1"), new Product("D", "Category2"));

Map<String, List<Product>> result = productList.stream().collect(Collectors.groupingBy(Product::getCategory));
System.out.println(result);

// 输出:
// {Category1=[Product{name='A', category='Category1'}, 
// Product{name='C', category='Category1'}], 
// Category2=[Product{name='B', category='Category2'}, 
// Product{name='D', category='Category2'}]}

3. 聚合收集器

将流中的元素聚合成一个值。Java 提供的聚合收集器有以下几种:

  • reducing():对流中的元素进行聚合,最终返回一个 Optional;
  • summarizingInt()、summarizingDouble()、summarizingLong():将流中的元素转换为 Int、Double 或 Long 类型,然后进行汇总操作。

以下是 reducing() 和 summarizingInt() 的示例代码:

// 使用 reducing() 计算元素的总和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream().collect(Collectors.reducing(Integer::sum));
System.out.println(sum); // Optional[15]

// 使用 summarizingInt() 计算元素的平均值、最大值、最小值、总和等
List<Integer> numbers2 = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers2.stream().collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(stats.getAverage()); // 3.0
System.out.println(stats.getMax()); // 5
System.out.println(stats.getMin()); // 1
System.out.println(stats.getSum()); // 15

收集器的使用

1. 如何定义自定义收集器

我们可以通过实现 Collector 接口来定义自己的收集器。Collector 接口定义了四个方法:

  • supplier():提供一个空的累加器(即初始容器),用于累加收集的元素;
  • accumulator():将元素 T 添加到累加器 A 中,并且在添加完后,累加器必须按原有顺序无副作用的处理前一个累加器,然后返回一个新的累加器;
  • combiner():将 A1 和 A2 两个累加器合并成一个新的累加器,通常与accumulator() 方法相反,其中 A2 代表 accumulator() 方法返回的新容器,A1 代表旧容器;
  • finisher():对最终容器进行转换,例如将累加器转换成期望的返回类型,或对其进行最终的变换。

下面是示例代码:

class Person {
    private String name;
    private int age;
    // 省略 getters 和 setters 方法
}

class PersonStatistics {
    int count;
    int totalAge;
    double averageAge;

    public PersonStatistics() {
        this.count = 0;
        this.totalAge = 0;
        this.averageAge = 0.0;
    }

    public void accept(Person person) {
        count++;
        totalAge += person.getAge();
        averageAge = (double) totalAge / (double) count;
    }

    public PersonStatistics combine(PersonStatistics other) {
        count += other.count;
        totalAge += other.totalAge;
        averageAge = (double) totalAge / (double) count;
        return this;
    }

    public void finish() {
        // do nothing
    }

    public double getAverageAge() {
        return this.averageAge;
    }

    // 省略 toString() 方法
}

class PersonCollector implements Collector<Person, PersonStatistics, PersonStatistics> {

    @Override
    public Supplier<PersonStatistics> supplier() {
        return PersonStatistics::new;
    }

    @Override
    public BiConsumer<PersonStatistics, Person> accumulator() {
        return PersonStatistics::accept;
    }

    @Override
    public BinaryOperator<PersonStatistics> combiner() {
        return PersonStatistics::combine;
    }

    @Override
    public Function<PersonStatistics, PersonStatistics> finisher() {
        return personStatistics -> {
            personStatistics.finish();
            return personStatistics;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.emptySet();
    }
}

List<Person> personList = Arrays.asList(
        new Person("Hank", 33),
        new Person("Charlie", 31),
        new Person("Emily", 26),
        new Person("Michael", 58)
);

PersonStatistics personStatistics = personList.stream().collect(new PersonCollector());
System.out.println(personStatistics);

输出结果:

PersonStatistics{count=4, totalAge=148, averageAge=37.0}

2. 如何使用自定义收集器

要使用自定义收集器,需要将其传递给 Stream 的 collect() 方法。以下是示例代码:

List<Person> personList = Arrays.asList(
        new Person("Hank", 33),
        new Person("Charlie", 31),
        new Person("Emily", 26),
        new Person("Michael", 58)
);

PersonStatistics personStatistics = personList.stream().collect(new PersonCollector());
System.out.println(personStatistics);

输出结果:

PersonStatistics{count=4, totalAge=148, averageAge=37.0}

小结

Java 中的收集器是处理流中数据的重要工具。本文主要介绍了收集器的基本概念、分类、使用方法和自定义方法。在实际的开发过程中,需要根据具体业务需求来选择不同的收集器。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 流处理之收集器详解 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • 微信小程序js文件改变参数并在视图上及时更新【推荐】

    针对这个问题,我为您提供以下完整攻略: 问题背景 在微信小程序开发中,我们通常需要在视图中传递参数,并且这些参数可能会随着操作或者其他因素发生变化。如果我们希望在参数发生变化的时候,及时更新视图,该怎么做呢? 解决方案 一种通用的解决方案是使用小程序提供的相应生命周期函数,根据参数的变化更新视图。具体实现方式如下: 1. 在wxml文件中绑定数据 首先需要在…

    Java 2023年5月23日
    00
  • Java中char[]输出不是内存地址的原因详解

    题目:Java中char[]输出不是内存地址的原因详解 为什么Java中char[]数组的输出结果不是内存地址呢?这个问题很多Java初学者都会遇到,下面就给大家详细讲解Java中char[]数组的特性。 char[]数组在Java中的特性 Java中的char[]数组与其他基本数据类型数组一样,是一种在内存中开辟空间的一维数组,用来存储相应的数据。 cha…

    Java 2023年5月26日
    00
  • Springboot快速入门教程

    下面是关于“Springboot快速入门教程”的完整攻略。 1. 前置条件 在开始学习Springboot之前,需要具备一定的Java基础知识,并熟悉Spring框架的基本概念。 2. 学习步骤 2.1 创建项目 在开始使用Springboot开发项目前,需要先创建一个基础的Springboot项目。在这里以使用Maven创建项目为例: <groupI…

    Java 2023年5月15日
    00
  • Java开发之spring security实现基于MongoDB的认证功能

    Java开发之spring security实现基于MongoDB的认证功能 介绍 本文将详细介绍如何使用Spring Security实现基于MongoDB的认证功能,包括用户注册、登录、忘记密码等功能。Spring Security是一个开源框架,旨在为Java应用提供身份验证和授权保护。MongoDB是一种基于文档的非关系型数据库,它的内容通常以JSO…

    Java 2023年5月20日
    00
  • Java Date时间类型的操作实现

    下面我将详细的讲解Java Date时间类型的操作实现的完整攻略。 操作Java Date类型 Java中的Date类表示一个特定的时间点,可以用于记录日期时间。下面是一些Java Date操作的方法。 创建Java Date 我们可以使用Date()构造函数来创建Date对象,如下所示: Date date = new Date(); 这将返回当前日期和时…

    Java 2023年5月20日
    00
  • 在无界面centos7上部署jdk和tomcat的教程

    在无界面CentOS 7上部署JDK和Tomcat教程 在无界面CentOS 7上部署JDK和Tomcat可以提供Web应用程序的基本运行环境,在本文中将介绍完整的部署过程。 安装Java JDK 从Oracle官网下载适用于Linux的JDK安装包(.tar.gz格式)。您可以将其下载到任何地方,我们将假设您将其下载到名为/usr/local的根目录下。以…

    Java 2023年5月19日
    00
  • 解析MySql与Java的时间类型

    下面是“解析MySql与Java的时间类型”的完整攻略。 1. MySql时间类型 MySql中定义了多种时间类型,包括日期时间、时间戳、时间等。下面分别介绍不同时间类型的定义及其在Java中的映射类型。 1.1. DATETIME类型 DATETIME类型表示年、月、日、小时、分钟、秒。格式为:YYYY-MM-DD HH:MM:SS。 在Java中,可以使…

    Java 2023年5月20日
    00
  • java自定义日志输出文件(log4j日志文件输出多个自定义日志文件)

    以下是详细的java自定义日志输出文件(log4j日志文件输出多个自定义日志文件)的攻略: 1.安装log4j 我们在进行自定义日志输出文件之前,需要先安装一个java非常常用的日志库—log4j,安装的步骤如下:1. 前往Apache Log4j官网(https://logging.apache.org/log4j/2.x/)下载最新版的log4j。2. …

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部