Java 流收集器 ( Stream Collectors ) ( 一 ) - 统计计算

yufei       6 年, 3 月 前       2022

首先需要声明的是,我的开发语言是 PHP,但我对所有语言都懂一点点,然后很多文章,都是看到某个知识点就想写些什么

刚刚再浏览某些文章的时候看到 Java 的流收集器 ( Stream Collectors ),哈哈,瞬间感兴趣,流收集器,光这四个大字看起来就有点高大上的直觉

流收集器 ( Stream Collectors )

众所周知,流 ( Stream ) 是 Java 8 引入的一个新的抽象,让我们可以以声明的方式处理数据。此外,流天生可以利用多核构架,而无需编写单线多线程代码

CollectorsCollector 接口的一个实现类,它实现了各种有用的 reduce 操作,例如将元素累积到集合中,根据各种标准汇总元素等

引入命令空间

对了,为了方便演示,我们直接用单个文件来演示,文件名为 MyCollectors.java

MyCollectors.java

import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
import java.util.stream.Collectors;

使用 Collectors 类

为了演示流收集器 Collectors 的用法,我们首先定义一个雇员类 Employee 来保存数据

MyCollectors.java

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Employee {
    private String empId;
    private String name;
    private Double salary;
    private String department;

    public Employee(String empId, String name, Double salary, String department) {
        this.empId = empId;
        this.name = name;
        this.salary = salary;
        this.department = department;
    }
    public String getEmpId() {
        return empId;
    }

    public void setEmpId(String id ) {
        empId = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String myname ) {
        name = myname;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(double mysalary) {
        salary = mysalary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String mydepartment ) {
        department = mydepartment;
    }

    public String toString() {
        return "Employee(" + empId + "," + name + "," + salary.toString() + "," + department + ")";
    }
}

然后,我们创建多个 Employee 类的实例,并把它们都放到列表 employees

class MyCollectors {

    public static void main(String[] args) {

        Employee john = new Employee("E123", "John Nhoj", 200.99, "IT");
        Employee south = new Employee("E223", "South Htuos", 299.99, "Sales");
        Employee reet = new Employee("E133", "Reet Teer", 300.99, "IT");
        Employee prateema = new Employee("E143", "Prateema Rai", 300.99, "Benefits");
        Employee yogen = new Employee("E323", "Yogen Rai", 200.99, "Sales");

        List<Employee> employees = Arrays.asList(john, south, reet, prateema, yogen);

        System.out.println(employees);
    }
}

使用命令 javac MyCollectors.java && java MyCollectors 运行,输出结果如下

$ javac MyCollectors.java && java MyCollectors
[Employee(E123,John Nhoj,200.99,IT), Employee(E223,South Htuos,299.99,Sales), Employee(E133,Reet Teer,300.99,IT), Employee(E143,Prateema Rai,300.99,Benefits), Employee(E323,Yogen Rai,200.99,Sales)]

好,有了演示数据就好办了,接下来我们使用流收集器来做一些有趣的操作

Collectors.averagingDouble 求取所有雇员薪水的平均数

Double averageSalary = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(averageSalary);

输出结果 260.79

相类似的方法还有

求取 Integer 类型的平均值

public static <T> Collector<T,?,Double> averagingInt(ToIntFunction<? super T> mapper)

求取 Long 类型的平均值

public static <T> Collector<T,?,Double> averagingLong(ToLongFunction<? super T> mapper)

Collectors.summingDouble() 求取所有雇员薪水的总额

Double totalSalary = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(totalSalary);

输出结果为 1303.95

同样的,相类似的方法还有

求取 Interger 类型的总和

public static <T> Collector<T,?,Integer> summingInt(ToIntFunction<? super T> mapper)

求取 Long 类型的总和

public static <T> Collector<T,?,Long> summingLong(ToLongFunction<? super T> mapper)

查找薪水最大值的雇员

为了查找薪水的最大值,我们就要做以系列的调用了

Double maxSalary = employees.stream().collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)), emp -> emp.get().getSalary()));
System.out.println(maxSalary);

输出结果为 300.99

这个调用看起来适不适合很头晕 ? 又长又臭

collectingAndThen() 方法收集收据,使用第二个参数来格式化数据,然后再传递给第一个参数来处理,原型如下

Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)

其中第二个参数 finisher 用于格式化数据,比如我们求取薪水的平均值,并添加美元符号和保留 3 位小数,那么可以使用下面的语句

String avgSalary2 = employees.stream().collect(Collectors.collectingAndThen(Collectors.averagingDouble(Employee::getSalary), new DecimalFormat("$0.000")::format));
System.out.println(avgSalary2);

输出结果为 $260.790

对了,使用 DecimalFormat 需要引入类 java.text.DecimalFormat

一次性统计平均值,总和,最大值,最小值 Collectors.summarizingDouble

我们可以使用 Collectors.summarizingDouble() 函数一次性统计 Double 类型数据的平均值,总和,最大值,最小值

DoubleSummaryStatistics statistics = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("Average: " + statistics.getAverage() + ", Total: " + statistics.getSum() + ", Max: " + statistics.getMax() + ", Min: "+ statistics.getMin());

输出结果为 Average: 260.79, Total: 1303.95, Max: 300.99, Min: 200.99

Collectors.summarizingDouble() 方法返回的是 DoubleSummaryStatistics 类型的实例,所以需要引入类 java.util.DoubleSummaryStatistics

相类似的方法还有

一次性计算 Integer 类型的统计信息

public static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)

一次新计算 Long 类型的统计信息

public static <T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)

结束语

本章节已经有点长了,我们就到此结束吧,贴上以上范例的所有代码

import java.util.Arrays;
import java.util.List;
import java.util.Comparator;
import java.text.DecimalFormat;
import java.util.stream.Collectors;
import java.util.DoubleSummaryStatistics;

class Employee {
    private String empId;
    private String name;
    private Double salary;
    private String department;

    public Employee(String empId, String name, Double salary, String department) {
        this.empId = empId;
        this.name = name;
        this.salary = salary;
        this.department = department;
    }
    public String getEmpId() {
        return empId;
    }

    public void setEmpId(String id ) {
        empId = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String myname ) {
        name = myname;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(double mysalary) {
        salary = mysalary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String mydepartment ) {
        department = mydepartment;
    }

    public String toString() {
        return "Employee(" + empId + "," + name + "," + salary.toString() + "," + department + ")";
    }
}

class MyCollectors {

    public static void main(String[] args) {

        Employee john = new Employee("E123", "John Nhoj", 200.99, "IT");
        Employee south = new Employee("E223", "South Htuos", 299.99, "Sales");
        Employee reet = new Employee("E133", "Reet Teer", 300.99, "IT");
        Employee prateema = new Employee("E143", "Prateema Rai", 300.99, "Benefits");
        Employee yogen = new Employee("E323", "Yogen Rai", 200.99, "Sales");

        List<Employee> employees = Arrays.asList(john, south, reet, prateema, yogen);

        System.out.println(employees);

        Double averageSalary = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(averageSalary);

        Double totalSalary = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(totalSalary);

        Double maxSalary = employees.stream().collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)), emp -> emp.get().getSalary()));
        System.out.println(maxSalary);

        String avgSalary2 = employees.stream()
        .collect(Collectors.collectingAndThen(Collectors.averagingDouble(Employee::getSalary), new DecimalFormat("$0.000")::format));
        System.out.println(avgSalary2);


        DoubleSummaryStatistics statistics = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println("Average: " + statistics.getAverage() + ", Total: " + statistics.getSum() + ", Max: " + statistics.getMax() + ", Min: "+ statistics.getMin());

    }
}
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.