JAVA8学习7-Collector接口详解以及实现类

7 Collector 接口详解(collect 收集器、Collectors)*****************

  1. collect: 收集器

  2. Collector 作为 collect 方法的参数

  3. Collector 本身是一个接口,它是一个可变的汇聚操作,作用是将输入元素累积到一个可变的结果容器中(如ArrayList 是一个可变容器);(可选操作)它会在所有元素都处理完毕后,将累积的结果转换为一个最终的表示。它支持串行和并行两者方式。

  4. Collectors 本身提供了关于 Collector 的常见汇聚实现,Collectors 本身是一个工厂.

  5. Collector 接受四个函数进行共同工作,

    1. 第一个参数(supplier 新容器) :创建一个新的结果容器(Supplier supplier 不接受参数返回一个结果)
    2. 第二个参数(accumulator 累加器):将新的数据元素合并到一个结果容器中(BiConsumer accumulator 将一个值折叠到一个可变的结果容器中,其中 BiConsumer 函数式是用来接收两个参数,不返回值)
    3. 第三个参数(combiner 合并器 并发时用) :将两个部分结果容器合并成一个。可能将状态从一个参数折叠成另一个并且返回它,也可能返回一个新的容器(BinaryOperator combiner 接受两个同类型的参数,返回同类型的结果)
    4. 第四个参数(finisher 完成其):执行一个可选的最终转换,执行最终的转换,从中间累积的类型,转换成最终的类型(Function finisher)
  6. 为了确保串行和并行流操作结果的等价性,Collector函数需要满足两个条件:ideentity(同一性) 与 associativity(结合性)

  7. 同一性必须满足部分结果与空容器结合时等于部分结果:a ==combiner.apply(a, supplier.get() ); (其中a为部分累积结果) supplier.get() 获取的是空集合

    (List<String> list1, List<String> list2 ) -> {list1.addAll(list2);return list1}
    
  8. 结合性:分割计算必须得到一个等价的结果

    // t1 t2 是两个输入元素, r1 r2是两个执行结果
    A a1 = supplier.get();// 得到空的结果容器  a1
    accumulator.accept(a1, t1);// a1 是每一次累加之后的中间结果,t1 是带处理的下一个元素 
    accumulator.accept(a1, t2);
    R r1 = finisher.apply(a1);  // result without splitting
    //分割计算
    A a2 = supplier.get();
    accumulator.accept(a2, t1);
    A a3 = supplier.get();
    accumulator.accept(a3, t2);
    R r2 = finisher.apply(combiner.apply(a2, a3));  // result with splitting
    
  9. 对于无序的收集器,如果两个累积的结果是等价的判断是 finisher.apply(a1).equals(finisher.apply(a2)).对于无序的收集器,等同只会考虑包含相同的元素,忽略顺序。

combiner 函数 ,有4个线程同时去执行,那么久会生成四个部分结果,combine人函数的作用就是将四个部分结果合成一个。

1,2,3,4 四个部分结果,

返回一个新的容器:

1,2 -> 5

5,3 -> 6

6,4 -> 7

折叠成另一个并返回:

1, 2 -> 1

1,2,3,4 -> 1 就像是 flatMap 将1,2,3,4 平铺成一个stream

reduce 和 collect 两者很像,区别在于reduce结果集是个不可变的容器,而collect 是可变的容器,能用reduce实现的collect也可以实现,能用collect实现的 reduce 也可以,但是有一点非常重要,在并发过程中,不能使用reduce转collect会错乱

7.1实现Collector注意事项:

Collector<T, A, R>: T 是流中每个元素的类型。A是可变的累积类型即集合的类型。R: 表示汇聚结果的类型

Supplier<A> supplier(); // 结果容器的类型
BiConsumer<A, T> accumulator(); // A 是每次中间操作的类型,T是下一次待操作元素的类型
BinaryOperator<A> combiner(); // 合并两个部分结果,A类型
Function<A, R> finisher(); // 结果容器类型,最终给用户操作的类型 一般A是泛型(list),R是A的具体类型(list<String>)

创建一个收集器TreeSet:

Collector<Widget, ?, TreeSet<Widget>> intoSet = Collector.of(TreeSet::new, TreeSet::add, (left, right) -> { left.addAll(right); return left; });

7.2 collector 汇聚方式:

R container = collector.supplier().get();
*     for (T t : data)
*         collector.accumulator().accept(container, t);
*     return collector.finisher().apply(container);

7.3 collector 嵌套使用

​ 创建一个收集者计算员工流的薪酬总和:

 Collector<Employee, ?, Integer> summingSalaries = Collectors.summingInt(Employee::getSalary))

​ 如果我们想创建一个收集器来按部门列出工资总额,我们可以重复使用“工资总和”逻辑 Collectors.groupingBy(Function, Collector)

 Collector<Employee, ?, Map<Department, Integer>> summingSalariesByDept = Collectors.groupingBy(Employee::getDepartment, summingSalaries);

7.4 Collectors 收集器工厂

​ Collectors 工厂,向开发者提供常见的收集器。 静态内部类 collectorImpl实现 collector 接口。

源码例子:

	// Accumulate names into a List
	List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());

	// Accumulate names into a TreeSet
	Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));

	// Convert elements to strings and concatenate them, separated by commas
	String joined = things.stream().map(Object::toString)               .collect(Collectors.joining(", "));

	// Compute sum of salaries of employee
	int total = employees.stream()                    .collect(Collectors.summingInt(Employee::getSalary)));

	// Group employees by department
	Map<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));

	// Compute sum of salaries by department
	Map<Department, Integer> totalByDept = employees.stream()           .collect(Collectors.groupingBy(Employee::getDepartment,Collectors.summingInt(Employee::getSalary)));

	// Partition students into passing and failing
	Map<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

通过事例代码演示下,常用功能:

package cn.zxhysy.jdk8.stream2;

import java.util.*;
import java.util.stream.Collectors;

public class streamTest1 {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",80);
        Student student2 = new Student("lisi",90);
        Student student3 = new Student("wangwu",100);
        Student student4 = new Student("zhaoliu",90);
        Student student5 = new Student("zhaoliu", 90);

        List<Student> students = Arrays.asList(student1, student2, student3, student4,student5);

        // stream 中,一个问题提供了多种解决方案,提倡越具体的方式越好,通过方式和具体方式都能解决,选择具体方式

        students.stream()
                .collect(Collectors.minBy(Comparator.comparingInt(Student::getScore)))
                .ifPresent(System.out::println);
        students.stream()
                .collect(Collectors.maxBy(Comparator.comparingInt(Student::getScore)))
                .ifPresent(System.out::println);
        System.out.println(students.stream().collect(Collectors.averagingInt(Student::getScore)));
        System.out.println(students.stream().collect(Collectors.summingInt(Student::getScore)));
        IntSummaryStatistics intSummaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getScore));
        System.out.println(intSummaryStatistics);
        System.out.println("------------------");

        System.out.println(students.stream().map(Student::getName).collect(Collectors.joining()));
        System.out.println(students.stream().map(Student::getName).collect(Collectors.joining(", ")));
        System.out.println(students.stream().map(Student::getName).collect(Collectors.joining(", ","<begin>","<end>")));
        System.out.println("------------------");

        // 先根据分数分组,再根据名字分组
        Map<Integer, Map<String, List<Student>>> collect = students.stream().collect(Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getName)));
        System.out.println(collect);
        System.out.println("------------------");

        // 分区,找出大于80和小于等于80的
        Map<Boolean, List<Student>> map = students.stream().
                collect(Collectors.partitioningBy(student -> student.getScore() > 80));
        System.out.println(map);
        System.out.println("------------------");

        // 大于80分区,大于90分区
        Map<Boolean, Map<Boolean, List<Student>>> map2 = students.stream()
                .collect(Collectors.partitioningBy(student -> student.getScore() > 80,
                        Collectors.partitioningBy(student -> student.getScore() > 90)));
        System.out.println(map2);
        System.out.println("-------------------");

        Map<Boolean, Long> map1 = students.stream()
                .collect(Collectors.partitioningBy(student -> student.getScore() > 80,
                        Collectors.counting()));
        System.out.println(map1);
        System.out.println("-------------------");

        Map<String, Student> map3 = students.stream()
                .collect(Collectors
                        .groupingBy(Student::getName,
                                // 收集之后,返回Optional中的元素(minBy返回的是Optional,但既然有分组,那肯定有元素,我们就不需要判断Optional中是否有元素,手动将Optional拆开)
                                Collectors.collectingAndThen(
                                        Collectors.minBy(Comparator.comparingInt(Student::getScore)),
                                        Optional::get)));

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值