写点什么

集合处理的利器

作者:技术小生
  • 2022 年 7 月 05 日
  • 本文字数:1335 字

    阅读完需:约 4 分钟

集合处理的利器

为什么需要 Stream

集合在 Java 中使用最为广泛,几乎每个 Java 应用程序都会制造和处理集合,可以说集合是编程任务最基本的单元。但这样的基本单元常规性的操作(比如数据分组、筛选、排序等),并没有 API 支持。而且对于集合元素不存在逻辑关系时的处理,并没有一种并行方式来利用多核机构。

那么,Java 语言的设计者就设计了流,使每位工程师能更加轻松的处理集合、操作集合。


Stream 详解

Stream 是 Java8 提供的 API,以声明性方式处理数据集合,处理更加高效,可以对集合进行链状流式的操作。

Stream 的所有操作可以进行分类:

  1. 中间操作

中间操作只记录一种标记,只有终止操作才会触发实际计算。

  1. 无状态:指元素不受前面元素的影响,元素之间无关联

  2. 有状态:有状态的中间操作必须等到所有元素处理之后才知道最终结果

  3. 终止操作

终止操作就是得出最后计算结果的操作。

  1. 短路操作:不用处理全部元素就可以返回结果

  2. 非短路操作:必须处理所有元素才能得到最终结果


Stream 原理

Stream 内部是通过 Pipeline 的方式实现的,基本思想就是在迭代的时候顺着流水线尽可能的执行更多的操作,从而避免多次迭代。采用某种方式记录用户的每一步操作,当用户调用终止操作时将之前的记录操作叠加到一起,再一次迭代中全部执行。

其中,核心问题就是:

  1. 用户操作如何记录

  2. 操作如何叠加

  3. 叠加后的操作如何执行

操作如何记录


此图为 Stream 相关类和接口的继承关系,Stream 通过 Stage 来描述一个完整操作,并用某种实例化后的 PipelineHelper 来代表 Stage,将具有先后顺序的各个 Stage 连载一起,构成整个流水线。

在调用 Stream()方法时,会创建一个 Head 实例来表示流操作的头。紧接着调用一系列中间操作,不断产生新的 Stream。这些 Stream 对象以双向链表的形式组织起来,构成整个流水线。由于每个 Stage 都记录了前一个 Stage 和本次的操作以及毁掉函数,依靠这种结构就能建立起对数据源的所有操作。


操作如何叠加

通过 Slin 接口。每个 Stage 都将自己的操作封装在一个 Slink 里,前一个 Stage 只需调用后一个 Stage 的 accept()方法即可。实际上 Stream API 内部实现本质就是如何重写 Slink 接口的这四个方法。

  1. 首先 begin()方法告诉 Slink 参与操作的元素个数,方便确定中间结果容器的大小

  2. 之后通过 accept()方法将元素添加到中间结果当中,最终执行时调用者会不断调用该方法,直到遍历所有元素

  3. 最后 end()方法告诉 Slink 所有元素遍历完毕,启动对应操作步骤,操作完成后将结果传递给下游的 Slink

  4. 如果下游的 Slink 时短路操作,将结果传递给下游时不断询问 cancellationrequested()是否可以结束处理


叠加后操作如何执行

Stream 类库的设计者设置了一个Sink AbstractPipeline.opWrapSink(int flags, Sink downstream)方法来得到 Sink,该方法的作用是返回一个新的包含了当前 Stage 代表的操作以及能够将结果传递给 downstream 的 Sink 对象。

使用 opWrapSink()可以将当前操作与下游 Sink(上文中的 downstream 参数)结合成新 Sink。试想只要从流水线的最后一个 Stage 开始,不断调用上一个 Stage 的 opWrapSink()方法直到最开始(不包括 stage0,因为 stage0 代表数据源,不包含操作),就可以得到一个代表了流水线上所有操作的 Sink。流水线上从开始到结束的所有的操作都被包装到了一个 Sink 里,执行这个 Sink 就相当于执行整个流水线。


发布于: 2022 年 07 月 05 日阅读数: 20
用户头像

技术小生

关注

还未添加个人签名 2019.10.08 加入

还未添加个人简介

评论

发布
暂无评论
集合处理的利器_java8_技术小生_InfoQ写作社区