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)]}
从结果中可以看出,所有员工被分成了三个组,分别是 Sales
、Benefits
和 IT
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); } }