E06:用流收集数据

原创 翎野君 翎驿 2020-01-24

翎野君/文

内容概览

目录课程内容视频回看代码位置
E01通过行为参数化传递代码哔哩哔哩 ¦ Youtubebehavior-parameterization
E02Lambda表达式和函数式接口-上哔哩哔哩 ¦ Youtubelambda-expressions
E03Lambda表达式和函数式接口-下哔哩哔哩 ¦ Youtubelambda-expressions
E04介绍和引入流的概念哔哩哔哩 ¦ Youtubeintroducing-streams
E05使用流哔哩哔哩 ¦ Youtubeworking-with-streams
E06用流收集数据哔哩哔哩 ¦ Youtubecollect-data-with-streams
E07并行数据处理哔哩哔哩 ¦ Youtubeparallel-data-processing
E08default默认方法哔哩哔哩 ¦ Youtubedefault-methods
E09巧用Optional之优雅规避NPE问题哔哩哔哩 ¦ Youtubeoptional-alternative-null
E10新的日期和时间哔哩哔哩 ¦ Youtubenew-date-and-time

01.收集器简介

1.收集器介绍

我们在前一章中学到,流可以用类似于数据库的操作帮助我们处理集合。

Java 8中流支持两种类型的操作:中间操作(如filter或map)和终端操作(如count、findFirst、forEach和reduce)。

中间操作可以链接起来,将一个流转换为另一个流。这些操作不会消耗流,其目的是建立一个流水线。与此相反,终端操作会消耗流,以产生一个最终结果,例如返回流中的最大元素。

2.概念辨析

collect:Stream接口中定义的方法用来整合收集流操作的最终结果,其中的一个接口定义为。

<R, A> R collect(Collector<? super T, A, R> collector);

Collector:Collector是专门用来作为Stream的collect方法的参数的,Collector主要包含五个参数,它的行为也是由这五个参数来定义的。

public interface Collector<T, A, R> {
// supplier参数用于生成结果容器,容器类型为A
Supplier<A> supplier();
// accumulator用于消费元素,也就是归纳元素,这里的T就是元素,它会将流中的元素一个一个与结果容器A发生操作
BiConsumer<A, T> accumulator();
// combiner用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
BinaryOperator<A> combiner();
// finisher用于将之前整合完的结果R转换成为A
Function<A, R> finisher();
// characteristics表示当前Collector的特征值,这是个不可变Set
Set<Characteristics> characteristics();
}

Collectors:Collectors是一个工具类,是JDK预实现Collector的工具类,它内部提供了多种Collector,我们可以直接拿来使用。

Collection:Collection接口是 (java.util.Collection)是Java集合类的顶级接口之一,整个集合框架就围绕一组标准接口而设计。

02.使用Collector进行collect收集

Collector接口中的方法介绍

1. 建立新的结果容器: supplier方法

Supplier supplier(): supplier方法必须返回一个结果为空的Supplier,也就是一个无参数函数,在调用时它会创建一个空的累加器实例,供数据收集过程使用。

2. 将元素添加到结果容器: accumulator方法

BiConsumer<A, T> accumulator(): accumulator方法会返回执行操作的函数。当遍历到流中第n个元素时,这个函数执行时会有两个参数保存归约结果的累加器(已收集了流中的前n-1个项目),还有第n个元素本身。

3.对结果容器应用最终转换:finisher方法

Function<A, R> finisher(): 在遍历完流后,finisher方法必须返回在累积过程的,最后要调用的一个函数,以便将累加器对象转换为整个集合操作的最终结果。

4.合并两个结果容器:combiner方法

BinaryOperator combiner():四个方法中的最后一个——combiner方法会返回一个供归约操作使用的函数,它定义了对流的各个子部分进行并行处理时,各个子部分归约所得的累加器要如何合并。

5.characteristics方法

Set characteristics():

  • Characteristics是一个包含三个项目的枚举。
  • UNORDERED—— 结果不受流中项目的遍历和累积顺序的影响。
  • CONCURRENT—— accumulator函数可以从多个线程同时调用,且该收集器可以并行归约流。如果收集器没有标为UNORDERED,那它仅在用于无序数据源时才可以并行归约。
  • IDENTITY_FINISH——这表明完成器方法返回的函数是一个恒等函数,可以跳过。这种情况下,累加器对象将会直接用作归约过程的最终结果。这也意味着,将累加器A不加检查地转换为结果R是安全的。

执行流程

1.顺序归约过程的逻辑步骤
  1. 建立新的结果容器: supplier方法
  2. 将元素添加到结果容器: accumulator方法
  3. 对结果容器应用最终转换: finisher方法

图片

2.使用combiner来并行化归约过程

图片

示例

1.自己开发一个toList
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;

import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

/**
 * 重写toList()方法
 *
 * @param <T>
 */
public class ToListCollectorImpl<T> implements Collector<T, List<T>, List<T>> {

    /**
     * 初始化一个中间状态的结果容器
     *
     * @return
     */
    @Override
    public Supplier<List<T>> supplier() {
        return ListUtil::toList;
    }

    /**
     * 遍历流中的元素,把元素添加到中间状态的结果容器中
     *
     * @return
     */
    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return (a, t) -> a.add(t);
    }

    /**
     * 并行化规约过程中,进行合并操作
     *
     * @return
     */
    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }

    /**
     * 将中间状态的结果容器转换为最终的返回结果
     *
     * @return
     */
    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

    /**
     * 采用哪些优化建议
     *
     * @return
     */
    @Override
    public Set<Characteristics> characteristics() {
        Set<Characteristics> hashSet = CollUtil.newHashSet();
        hashSet.add(Characteristics.IDENTITY_FINISH);
        hashSet.add(Characteristics.CONCURRENT);
        return hashSet;
    }
}

我们迄今开发的ToListCollectorlmpl是IDENTITY_FINISH的,因为用来累积流中元素的List已经是我们要的最终结果,用不着进一步转换了。
但它并不是UNORDERED,因为用在有序流上的时候,我们还是希望顺序能够保留在得到的List中。最后,它是CONCURRENT的,但我们刚才说过了,仅仅在背后的数据源无序时才会并行处理。

2.collect其他调用形式

对于IDENTITY_FINISH的收集操作,还有一种方法可以得到同样的结果而无需从头实现新的Collectors接口。Stream接口中有一个重载的collect方法可以接受另外三个函数—supplier、accumulator和combiner,其语义和Collector接口的相应方法返回的函数完全相同。

 <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
                  
supplier:建立新的结果容器
accumulator:将元素添加到结果容器
combiner:合并两个结果的容器

List<String> stringList = Arrays.asList("abc", "def");
        String result = stringList.stream()
                .collect(() -> new StringBuilder(),
                        (s1, s2) -> s1.append(s2),
                        (r1, r2) -> r1.append(r2))
                .toString();                  

03.Collectors类

图片

图片

04.小结

  • collect是一个终端操作,它接受的参数是将流中元素累积到汇总结果的,各种方式(称为收集器)。
  • 预定义收集器包括将流元素归约和汇总到一个值,例如计算最小值、最大值或平均值。
  • 预定义收集器可以用groupingBy对流中元素进行分组,或用partitioningBy进行分区。
  • 你可以实现Collector接口中定义的方法来开发你自己的收集器。

05.配套源码地址

码云地址:https://gitee.com/javaprog/dating-java8-practice

转载:翎野君 - E06:用流收集数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值