Java Stream – Collectors类和collect()方法
使用Java流时,大多数情况下,我们会使用Collection作为流的源,但是我们也可以相反地进行操作,即从Stream获取Collection。为此,我们可以在Java Stream API中使用collect()方法。在此请注意,collect()方法对该流的元素执行可变的约简操作,该操作返回可变的结果容器。该可变结果容器可以是Collection类,例如ArrayList,HashSet或者StringBuilder等。
Java Stream中的collect()方法
collect()方法是一种终端操作,有两个重载的collect()方法。
1 <R,A> R collect(Collector <?super T,A,R>收集器)–使用收集器对该流的元素执行可变还原操作。
在方法类型中,参数为
T归约运算的输入元素类型
A –收集器的中间累积类型
R –结果类型
2- <R> R收集(Supplier <R>供应商,BiConsumer <R ,? super T>累加器,BiConsumer <R,R>组合器)–对此流的元素执行可变的归约运算。
该方法的参数如下:
可变结果容器的类型
providera函数,用于创建新的可变结果容器。它是供应商功能接口的一个实例。
累加的,关联的,无干扰的,无状态的功能,必须将元素折叠到结果容器中。它是BiConsumer功能接口的一个实例。
Combineran关联的,无干扰的,无状态的函数,它接受两个部分结果容器并将其合并,这些容器必须与累加器功能兼容。它是BiConsumer功能接口的一个实例。
Java Stream中的Collector类
在第一个collect()方法中,我们可以看到该参数的类型为Collector,它是java.util.stream包中的接口,并且定义了许多方法。
可以自己使用Collectors类,而不是自己实现这些方法,它是Collector的实现,并提供了许多实用程序简化方法,例如将元素累积到集合中,根据各种标准对元素进行汇总等。
Java收集器示例
在本节中,我们将看到一些将预定义的收集器与collect()方法一起使用的示例。
大多数示例使用Employee类的对象,这些对象具有字段name,dept,salary。
public class Employee { private String name; private String dept; private int salary; Employee(String name, String dept, int salary){ this.name = name; this.dept = dept; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } public String getDept() { return dept; } public void setDept(String dept) { this.dept = dept; } }
1.收集到列表中– Collectors.toList()
将所有员工姓名收集到列表中。
List<Employee> employeeList = new ArrayList<>(); employeeList.add(new Employee("Hyman", "Finance", 5500)); employeeList.add(new Employee("Lisa", "Finance", 5600)); employeeList.add(new Employee("Scott", "Finance", 7000)); employeeList.add(new Employee("Nikita", "IT", 4500)); employeeList.add(new Employee("Tony", "IT", 8000)); List<String> names = employeeList.stream() .map(e -> e.getName()) .collect(Collectors.toList()); names.forEach(System.out::println);
输出:
Hyman Lisa Scott Nikita Tony
2.收集到集合中– Collectors.toSet()
Set<String> names = employeeList.stream() .map(Employee::getName) .collect(Collectors.toSet()); names.forEach(System.out::println);
输出:
Tony Nikita Hyman Lisa Scott
3.收集到TreeSet中– Collectors.toCollection()
将所有员工姓名收集到TreeSet中以按顺序获取姓名。
Set<String> names = employeeList.stream() .map(Employee::getName) .collect(Collectors.toCollection(TreeSet::new)); names.forEach(System.out::println);
输出:
Hyman Lisa Nikita Scott Tony
4.收集到地图中– Collectors.toMap()
要使用toMap()方法将元素累积到Map中,我们需要提供两个函数
keyMapper此函数用于通过将功能应用于输入元素来获取键。
valueMapper此函数用于通过将函数应用于输入元素来获取值。
Map<String, Integer> names = employeeList.stream() .collect(Collectors.toMap(Employee::getName, Employee::getSalary)); names.entrySet().forEach(es->{System.out.println("Key- " + es.getKey() + " Value- " + es.getValue());});
输出:
Key- Tony Value- 8000 Key- Nikita Value- 4500 Key- Hyman Value- 5500 Key- Lisa Value- 5600 Key- Scott Value- 7000
5.将元素转换为字符串并将其连接起来
如果要将员工姓名显示为逗号分隔的字符串。
String names = employeeList.stream() .map(Employee::getName) .collect(Collectors.joining(", ")); System.out.println(names);
输出:
Hyman, Lisa, Scott, Nikita, Tony
6.计算sumCollectors.summingInt()
支付给员工的工资总和。
int totalSalary = employeeList.stream() .collect(Collectors.summingInt(Employee::getSalary)); System.out.println("Total salary paid to employees per month- " + totalSalary);
输出:
Total salary paid to employees per month- 30600
7.按fieldCollectors.groupingBy()分组
如果要按部门对员工进行分组,则返回值为Map。
Map<String, List<Employee>> names = employeeList.stream() .collect(Collectors.groupingBy(Employee::getDept)); names.entrySet().forEach(es->{System.out.println("Key- " + es.getKey()); System.out.println("Values"); es.getValue().forEach(e->System.out.println(e.getName()));});
输出:
Key- Finance Values Hyman Lisa Scott Key- IT Values Nikita Tony
- Collectors.partitioningBy
返回一个收集器,该收集器根据谓词对输入元素进行分区,并将它们组织成Map <Boolean,
列出<T >>。返回的Map始终包含false和true键的映射。对于真键,匹配给定谓词的那些元素将被映射,而不匹配给定谓词的元素将被映射到错误键下。
将员工分成薪水大于等于7000且小于等于7000的组。
Map<Boolean, List<Employee>> names = employeeList.stream() .collect(Collectors.partitioningBy(e -> e.getSalary() >= 7000));
输出:
Key- false Values Hyman Lisa Nikita Key- true Values Scott Tony
- Collectors.teeing
返回一个由两个下游收集器组成的收集器。传递给结果收集器的每个元素都由两个下游收集器处理,然后使用指定的合并功能将它们的结果合并为最终结果。 JDK 12中添加了此方法。
使用Collectors.teeing函数获取列表中元素的数量和总和。
List<Integer> listOfNumbers = Arrays.asList(11, 10, 9, 99, 98); List<String> list = listOfNumbers.stream().collect(Collectors.teeing(Collectors.counting(), Collectors.summingInt(n->Integer.valueOf(n.toString())), (a, s)->{List<String> l = new ArrayList<>(); l.add(a.toString()); l.add(s.toString()); return l;})); list.forEach(System.out::println);
输出:
5 227
- Collectors类中的摘要统计方法
有三种方法summarizingInt,summarizingLong和summarizingDouble返回结果值的摘要统计信息。
获取有关员工工资的摘要统计信息。
IntSummaryStatistics stats = employeeList.stream().collect(Collectors.summarizingInt(Employee::getSalary)); System.out.println("Sum of salaries - " + stats.getSum()); System.out.println("Average of salaries " + stats.getAverage()); System.out.println("Max salary " + stats.getMax()); System.out.println("Min salary " + stats.getMin());
输出:
Sum of salaries - 30600 Average of salaries 6120.0 Max salary 8000 Min salary 4500
在Combiner中使用collect方法
这种形式的collect方法需要三个功能:供应商功能,用于构造结果容器的新实例;累加器功能,用于将输入元素合并到结果容器中;以及合并功能,用于将一个结果容器的内容合并到另一个结果容器中。
例如,将流中元素的整数表示形式收集到ArrayList中。
List<Integer> numbers = Stream.of(1, 2, 3, 4, 5).collect(ArrayList::new, ArrayList::add, ArrayList::addAll); numbers.forEach(System.out::println);
输出:
1 2 3 4 5
将所有员工姓名作为连接字符串,其中值之间用逗号分隔。
String concat = employeeList.stream().map(Employee::getName).collect( () -> new StringJoiner(",", "", ""), StringJoiner::add, StringJoiner::merge).toString(); System.out.println("Employee Names- " + concat);
输出:
Employee Names- Hyman,Lisa,Scott,Nikita,Tony