分组 Java8 流而不收集它

Posted

技术标签:

【中文标题】分组 Java8 流而不收集它【英文标题】:Grouping Java8 stream without collecting it 【发布时间】:2016-12-25 02:28:57 【问题描述】:

Java 8 中是否有任何方法可以将 java.util.stream.Stream 中的元素分组而不收集它们?我希望结果再次成为Stream。因为我必须处理大量数据甚至无限流,所以我无法先收集数据并再次流式传输结果。

所有需要分组的元素在第一个流中都是连续的。因此,我喜欢保持流评估惰性。

【问题讨论】:

如果您的数据已经“预先分组”(通过连续),为什么还需要分组形式?提供一些上下文可能有助于为这个问题提供更好的答案 你的意思是使用Collectors中的groupBy而不收集? 听起来像是队列的工作,而不是流。消耗队列中的连续元素,直到检测到下一个组的开始,将该组添加到包含该组的下一个队列。 我必须从一个流开始,因为我以这种方式获取数据(大多数时候,但并不总是在数据库中进行 JOIN 之后,但并不总是超出 jOOQ grin)和与消费者对象的合同也是一个流。我必须对数据进行分组以重新创建由连接表表示的两个实体的 1:n 关系。因此,我试图实现的是根据关系 1 侧的相同对象对数据库记录进行分组。在进一步的步骤中,我会将分组记录映射到此对象以及关系 n 侧的对象列表。 【参考方案1】:

使用标准 Stream API 无法做到这一点。一般来说,您不能这样做,因为将来总有可能出现属于任何已创建组的新项目,因此在处理所有输入之前,您无法将您的组传递给下游分析。

但是,如果您事先知道要分组的项目在输入流中总是相邻的,您可以使用增强 Stream API 的第三方库来解决您的问题。其中一个库是StreamEx,它是免费的,由我编写。它包含许多“部分归约”运算符,这些运算符根据某些谓词将相邻项折叠成单个项。通常你应该提供一个BiPredicate 来测试两个相邻的项目,如果它们应该组合在一起,则返回true。下面列出了一些部分归约操作:

collapse(BiPredicate):用组的第一个元素替换每个组。例如,collapse(Objects::equals) 可用于从流中删除相邻的重复项。 groupRuns(BiPredicate):用组元素列表替换每个组(因此StreamEx<T> 转换为StreamEx<List<T>>)。例如,stringStream.groupRuns((a, b) -> a.charAt(0) == b.charAt(0)) 将创建字符串列表流,其中每个列表包含以相同字母开头的相邻字符串。

其他部分归约操作包括intervalMaprunLengths()等。

所有部分归约操作都是惰性的、对并行友好且非常高效。

请注意,您可以使用 StreamEx.of(stream) 从常规 Java 8 流轻松构造 StreamEx 对象。还有一些方法可以从数组、Collection、Reader 等构造它。StreamEx 类实现了Stream 接口,并且与标准 Stream API 100% 兼容。

【讨论】:

我去看看你的图书馆。这似乎正是我所需要的。谢谢你的建议。 嗯,很有趣。很高兴看到这应用于 OP 的实际代码,@MatthiasWimmer

以上是关于分组 Java8 流而不收集它的主要内容,如果未能解决你的问题,请参考以下文章

Java8新特性之:流

2020了你还不会Java8新特性?收集器比较器用法详解及源码剖析

为 AWS C# S3 SDK 使用内存流而不是文件流而不将完整文件写入 S3

《Java8实战》 - 读书笔记 - Stream流操作2:用流收集数据

如何在垃圾收集的 Obj-C 中保留一个窗口而不持有指向它的指针?

Java8中Stream的归约与收集