Java 流处理之收集器详解

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

阅读剩余 78%

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

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

相关文章

  • Spring Boot 2.2 正式发布,大幅性能提升 + Java 13 支持

    Spring Boot 是基于 Spring 快速开发应用程序的框架,可以极大地简化 Spring 应用的初始搭建以及开发过程中的烦琐配置。2019年10月17日,Spring Boot 2.2 正式发布,不仅大幅度提升了性能,还支持最新的 Java 13 版本。下面我们详细讲解 Spring Boot2.2 的完整攻略。 1. Spring Boot 2.…

    Java 2023年5月15日
    00
  • Java基础之数组详解

    Java基础之数组详解 什么是数组 数组是一种存储多个相同类型元素的数据类型。在Java中,数组的大小是在创建时确定的,并且在程序运行时不能改变。数组元素可以是任何基本类型、引用类型或其他数组类型。数组是一个对象,因此在Java中,数组变量的值是数组的引用。 创建数组 可以使用new关键字创建一个数组。语法如下: type[] arrayName = new…

    Java 2023年5月26日
    00
  • spring boot tomcat jdbc pool的属性绑定

    关于“spring boot tomcat jdbc pool的属性绑定”的完整攻略,我可以从以下几个方面进行讲解: 引入依赖 首先,我们需要在pom.xml文件中引入相关的依赖,在该文件中添加如下内容: <dependency> <groupId>org.springframework.boot</groupId> &l…

    Java 2023年5月19日
    00
  • 基于jsp+servlet实现的简单博客系统实例(附源码)

    这篇文章将会详细讲解如何通过使用JSP和Servlet来实现一个简单的博客系统。我们将会从搭建环境开始,到完成整个系统的构建。 环境搭建 工具准备 在开始之前,我们需要准备以下工具: Java SE Development Kit(JDK) Eclipse IDE for Java EE Developers Tomcat服务器 创建项目 打开Eclipse…

    Java 2023年6月15日
    00
  • Spring Data JPA 之 JpaRepository的使用

    下面将为您详细讲解Spring Data JPA之JpaRepository的使用攻略。 什么是JpaRepository? JpaRepository是Spring Data JPA提供的一个接口,它继承自PagingAndSortingRepository接口,同时它还继承了CrudRepository接口,提供了一些与业务相关的方法,如save、del…

    Java 2023年5月20日
    00
  • java面向对象的六原则一法则小结

    下面是讲解“Java面向对象的六大原则一法则小结”的攻略: 1. 单一职责原则 单一职责原则(Single Responsibility Principle,SRP)是指一个类只负责一个功能领域中的相关职责,或者说一个类只有一个引起它变化的原因。这个原则是实现高内聚、低耦合的关键,可以避免因为某个职责变化而引起整个类的变化,提高代码的可维护性、可扩展性。 示…

    Java 2023年5月26日
    00
  • JSONObject按put顺序排放与输出方式

    下面是有关”JSONObject按put顺序排放与输出方式”的攻略。 什么是JSONObject JSONObject是Java中的一个类,可以用于存储和操作JSON格式的数据。它能够将Java对象转换成JSON格式的字符串,也可以将JSON格式的字符串转换成Java对象。 JSONObject按put顺序排放 JSONObject是一种无序的数据结构,它并…

    Java 2023年5月26日
    00
  • 一篇文章带你了解java Object根类中关于toString,equals的方法

    下面是关于Java Object类中toString和equals方法的详细讲解: 什么是Java Object根类 在Java中,所有类的祖先都是Object类。因此,Object类可以被看作是Java中的根类。它定义了Java中最通用的基本方法,例如toString和equals方法。 toString方法详解 在Java中,toString方法是Obj…

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