Java 流收集器 ( Stream Collectors ) ( 三 ) - 分组元素 ( grouping )

yufei       6 年, 3 月 前       6686

Java 流收集器异常强大的第三个功能在于可以分组元素,然后再做一系列的计算

Collectors.groupingBy() 分组

Collectors 提供了 groupingBy() 用于分组元素,该方法接受两个参数,就是分组器和分组之后要做的进一步处理,然后返回一个 Map<K,List<T>>> 的哈希表

原型如下

Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)

NND. 看 Java 的方法原型,每次都想砸电脑的冲动,这泛型怎么感觉这么复杂

例如,如果我们想要把所有的雇员按照部门 department 分组,则可以如下实现

Map<String, List<Employee>> deptEmps = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
System.out.println(deptEmps);

因为用到了 Map,所以需要引入包 java.util.Map

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

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

从结果中可以看出,所有员工被分成了三个组,分别是 SalesBenefitsIT

Collectors.counting() 计数

groupingBy() 的原型中可以看出,该方法还接受第二个参数,就是分组后的进一步处理,我可以利用这个参数做一些其它的事情,比如计数 counting()

Map<String, Long> deptEmpsCount = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));
System.out.println(deptEmpsCount);

运行结果如下

{Sales=2, Benefits=1, IT=2}

比如我们可以使用 averagingDouble() 方法来计算每个部门的薪水平均值

Map<String, Double> averageSalaryDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.averagingDouble(Employee::getSalary)));
System.out.println(averageSalaryDept);

运行结果如下

{Sales=250.49, Benefits=300.99, IT=250.99}

但是,如果我们想给返回的结果的键值对排序,比如按照字母排序,要怎么做呢 ?

不用着急,哈哈,groupingBy() 方法还有第三种重载方法,它接受三个参数

分组排序再计算

三个参数的 groupingBy() 方法的原型如下

Collector<T,?,M> groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)

对于这个重载方法,我们可以简单的用六个字来归纳功能 分组预处理再计算,预处理可以是 排序

比如我们要对雇员按照部门分组,然后对部门进行排序,最后计算部门的平均薪水,可以如下实现

Map<String, Double> averageSalaryDeptSorted = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment, TreeMap::new, Collectors.averagingDouble(Employee::getSalary)));
System.out.println(averageSalaryDeptSorted);

因为使用了 TreeMap ,所以需要引入 java.util.TreeMap

运行结果如下

{Benefits=300.99, IT=250.99, Sales=250.49}

并行分组处理

多核时代,对于分组这种功能,没有充分利用多核功能是否有点太可惜了,Java 估计就是这么认为的,所以提供了并行分组方法 groupingByConcurrent()

groupingByConcurrent() 方法会返回一个 ConcurrentHashMap 类型的数据

该方法使用方式和 groupingBy 一样,我们就直接上范例吧

Map<String, Long> deptEmpCount3 = employees.stream().collect(Collectors.groupingByConcurrent(Employee::getDepartment, Collectors.counting())); 
System.out.println(deptEmpCount3)

运行结果如下

{Sales=2, IT=2, Benefits=1}

后记

关于流收集器的分组功能就这些了,下面贴出所有范例的代码

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
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);

        Map<String, List<Employee>> deptEmps = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
        System.out.println(deptEmps);

        Map<String, Long> deptEmpsCount = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting()));
        System.out.println(deptEmpsCount);

        Map<String, Double> averageSalaryDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.averagingDouble(Employee::getSalary)));
        System.out.println(averageSalaryDept);

        Map<String, Double> averageSalaryDeptSorted = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment, TreeMap::new, Collectors.averagingDouble(Employee::getSalary)));
        System.out.println(averageSalaryDeptSorted);

        Map<String, Long> deptEmpCount3 = employees.stream().collect(Collectors.groupingByConcurrent(Employee::getDepartment, Collectors.counting())); 
        System.out.println(deptEmpCount3);
    }
}
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

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

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