📜  如何在Java中构建自定义收集器?

📅  最后修改于: 2022-05-13 01:55:21.671000             🧑  作者: Mango

如何在Java中构建自定义收集器?

Java Collector是一个实用类,它为收集器接口提供了许多有用的方法和函数。主要收集器实现与流 collect() 方法一起使用。收集器接口由Java 8 在新引入的Java Stream API 的一部分下提供。该接口提供了各种方法来执行相互归约操作。可变归约操作对流中的数学信息执行算术函数,以找到最小、最大、平均或将 Stream 的元素累积到 Set 或连接 Stream 中的所有 String 元素。

创建自定义收集器的步骤

要编写自定义收集器,我们应该使用下面提到的方法来实现接口。收集通常发生在 Streams API 提供的四个步骤中。

  • Supplier():这是元素收集过程的第一步,在这个过程中,创建容器来保存流中的元素。该方法的返回类型是容器类型的提供者。
  • Accumulator():这是将每个元素添加到由供应商步骤创建的容器中的第二步。在此方法中,我们必须返回一个 BiConsumer函数,该函数接受容器和 Stream 中的每个元素。
  • Combiner() :这是可选步骤,仅在并行时代处理流时执行,如果Stream是顺序的,则跳过此步骤。组合步骤用于将所有元素组合到一个容器中。在这个方法中,我们应该返回一个组合两个累积容器的二元运算符函数。
  • Finish():这是收集过程的最后一步。只有当流中的所有元素都成功累积在供应商容器中时,它才会执行。在这个方法中,我们可以返回一个函数,将累加合并的容器转化为最终输出。

除了这些方法之外我们还有特性()方法来指定收集器的特性和一组特性的返回类型 Enum 值。

例子

为了说明这个例子,我们将代码分成三个部分以便更好地理解。让我们举一个例子,我们有一个 Employee 对象列表,我们想从它创建一个流并将 Employee 对象收集为一个不可变的 Triplets 列表。列表中的每个三元组将代表一个员工,并且包含员工的年龄、名字和姓氏。

Java
public class Employee_GFG {
    private long id;
    private String firstName;
    private String lastName;
    private int year;
  
    public Employee_GFG(long id, String firstName,
                        String lastName, int year)
    {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.year = year;
    }
  
    public long getId() { return id; }
  
    public void setId(long id) { this.id = id; }
  
    public String getFirstName() { return firstName; }
  
    public void setFirstName(String firstName)
    {
        this.firstName = firstName;
    }
  
    public String getLastName() { return lastName; }
  
    public void setLastName(String lastName)
    {
        this.lastName = lastName;
    }
  
    public int getYear() { return year; }
  
    public void setYear(int year) { this.year = year; }
}


Java
package com.example.geeksforgeeks.customcollector;
  
import java.util.ArrayList;
import java.util.Collections;
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;
import org.javatuples.Triplet;
  
public class EmployeeCollector implements
  
    Collector >,
              List > > {
  
    public static EmployeeCollector toEmployeeList()
    {
        return new EmployeeCollector();
    }
  
    public Supplier<
        List > >
    supplier()
    {
        return ArrayList::new;
    }
  
    public BiConsumer<
        List >, Student>
    accumulator()
    {
        return (list, student)
                   -> list.add(
                       Triplet.with(student.getYear(),
                                    student.getFirstName(),
                                    student.getLastName()));
    }
  
    public BinaryOperator<
        List > >
    combiner()
    {
        return (list1, list2) ->
        {
            list1.addAll(list2);
            return list1;
        };
    }
  
    @Override
    public Function<
        List >,
        List > >
    finisher()
    {
        return Collections::unmodifiableList;
    }
  
    @Override public Set characteristics()
    {
        return Set.of(Characteristics.UNORDERED);
    }
}


Java
public static void main(String[] args)
{
    List Employee = List.of(
        new Employee(1, "samarth", "sharma", 2019),
        new Employee(2, "praful", "john", 2019),
        new Employee(3, "goutam", "verma", 2015),
        new Employee(4, "mohit", "verma", 2016));
  
    List > listOfTriples
        = Employee.stream().collect(
            EmployeeCollector.toEmployeeList());
    listOfTriples.forEach(System.out::println);
}


在上面提到的例子中,我们有一个名为 Employee_GFG 的类,由名为 id、firstname、lastname、year、Constructor 的私有变量组成,它初始化变量,还有各种函数 setFirstName、getLastName、set LastName、getYear、setYear 等。这个函数只需设置或获取变量的值。

现在我们必须实现 Collector 接口。为供应商、累加器和整理器的收集器方法创建类,如上所述。

Java

package com.example.geeksforgeeks.customcollector;
  
import java.util.ArrayList;
import java.util.Collections;
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;
import org.javatuples.Triplet;
  
public class EmployeeCollector implements
  
    Collector >,
              List > > {
  
    public static EmployeeCollector toEmployeeList()
    {
        return new EmployeeCollector();
    }
  
    public Supplier<
        List > >
    supplier()
    {
        return ArrayList::new;
    }
  
    public BiConsumer<
        List >, Student>
    accumulator()
    {
        return (list, student)
                   -> list.add(
                       Triplet.with(student.getYear(),
                                    student.getFirstName(),
                                    student.getLastName()));
    }
  
    public BinaryOperator<
        List > >
    combiner()
    {
        return (list1, list2) ->
        {
            list1.addAll(list2);
            return list1;
        };
    }
  
    @Override
    public Function<
        List >,
        List > >
    finisher()
    {
        return Collections::unmodifiableList;
    }
  
    @Override public Set characteristics()
    {
        return Set.of(Characteristics.UNORDERED);
    }
}

在上面提到的示例中,我们创建了一个名为 EmployeeCollector 的类。在此基础上,我们使用三元组 Integer、Integer、String 和编写的 Supplier函数实现了一个自定义收集器,该函数提供一个容器来保存 Stream 元素并在调用它时提供一个新的 ArrayList。此返回函数中的 BiConsumer函数从流中获取容器,它是一个 ArrayList 和一个 Student 对象,BinaryOperator函数是组合器,它将容器作为参数并返回一个,最后一秒的函数名称“函数”是一个 ArrayList 的容器学生三胞胎并返回一个不可修改的列表。

现在,我们将在员工流上使用我们的收集器。我们将在 collect() 方法中使用 toEmployeeList()函数。

Java

public static void main(String[] args)
{
    List Employee = List.of(
        new Employee(1, "samarth", "sharma", 2019),
        new Employee(2, "praful", "john", 2019),
        new Employee(3, "goutam", "verma", 2015),
        new Employee(4, "mohit", "verma", 2016));
  
    List > listOfTriples
        = Employee.stream().collect(
            EmployeeCollector.toEmployeeList());
    listOfTriples.forEach(System.out::println);
}

输出

[1,samarth,sharma,2019]
[2,praful,john,2019]
[3,goutam,verma,2015]
[4,mohit,verma,2016]

上面是代码的所需输出,其中包含具有名字、姓氏、年份和 ID 的员工列表。