Java 8 流短路

Posted

技术标签:

【中文标题】Java 8 流短路【英文标题】:Java 8 stream short-circuit 【发布时间】:2015-11-18 11:58:42 【问题描述】:

阅读了一些关于 Java 8 的内容后,我在 this 博客文章中解释了一些关于流和它们的归约,以及何时可以将归约短路。在底部它指出:

注意在findFirstfindAny 的情况下,我们只需要与谓词匹配的第一个值(尽管findAny 不能保证返回第一个值)。但是,如果流没有排序,那么我们希望 findFirst 的行为类似于 findAny。操作allMatchnoneMatchanyMatch 可能根本不会使流短路,因为它可能需要评估所有值来确定运算符是true 还是false。因此,使用这些的无限流可能不会终止。

我知道findFirstfindAny 可能会使归约短路,因为一旦找到元素,就不需要进一步处理。

但是为什么 allMatchnoneMatchanyMatch 不可能呢?对于allMatch,如果你发现一个与谓词不匹配的,你可以停止处理。没有一样。 anyMatch 对我来说尤其没有意义,因为它几乎等于 findAny(除了返回的内容)?

说这三个可能不会短路,因为它可能需要评估所有值,也可以说findFirst/Any

我缺少一些根本的区别吗?我真的不明白发生了什么吗?

【问题讨论】:

“may not”如“might not”,而不是“not allowed to”。 @Andreas 是的,我只是不明白差异来自哪里。一切都可以按照我的看法短路吗? 【参考方案1】:

当 javadoc 说“可能不会短路”时,它只是指出它不是短路操作,并且根据值,可能会处理整个流。

另一方面,findFirstfindAny 保证短路,因为一旦满足它们就不需要处理流的其余部分。

【讨论】:

但它也可能发生在谓词上,只要它们不满足?【参考方案2】:

anyMatch、noneMatch 和 allMatch 返回布尔值,因此它们可能必须检查所有以证明逻辑。

findFirst 和 findAny 只关心找到他们能找到的第一个并返回它。

编辑: 对于给定的数据集,Match 方法保证总是返回相同的值,但 Find 方法却不是,因为顺序可能会改变并影响返回的值。

所描述的短路是指 Find 方法对给定数据集缺乏一致性。

【讨论】:

但是 anyMatch 也可以吗? 如果最后一个值是匹配的,那么是的。 Point 是 Match 方法总是在同一个数据集上返回相同的值,但 Find 方法不能保证一致性,因为顺序可能不同。 这种缺乏一致性就是所描述的短路【参考方案3】:

答案已更新

我会说博客文章是错误的,当它说“findFirst 或 findAny 我们只需要与谓词匹配的第一个值”时。

在allMatch(Predicate)、anyMatch(Predicate)、noneMatch(Predicate)、findAny() 和 findFirst() 的 javadoc 中:

这是一个短路终端操作。

但是,请注意findFirstfindAny 没有Predicate。所以他们都可以在看到第一个/任何值后立即返回。其他 3 个是有条件的,如果条件永远不会触发,则可能永远循环。

【讨论】:

来自博客:“操作 allMatch、noneMatch 和 anyMatch 可能根本不会短路流,因为它可能需要评估所有值来确定运算符是真还是假。”这显然是不正确的,因为一旦发现第一个不匹配项,就可以确定 allMatch 为假。 @MiserableVariable 正如我所说,博客文章是错误的。它们可以全部短路。 --- 该博客是正确的,其中 3 个可能可能永远运行,但它说的都是错误的。【参考方案4】:

根据 Oracle 的流文档: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps

如果在有无限输入时,终端操作可能在有限时间内终止,则它是短路的。在管道中进行短路操作是无限流处理在有限时间内正常终止的必要条件,但不是充分条件。

所有五个函数都有一行:

这是一个短路终端操作。

在函数的描述中。

【讨论】:

【参考方案5】:

有一个细微的差别,因为anyMatchfamily 使用谓词,而findAnyfamily 没有。从技术上讲,findAny() 看起来像 anyMatch(x -> true)anyMatch(pred) 看起来像 filter(pred).findAny()。所以这里我们有另一个问题。假设我们有一个简单的无限流:

Stream<Integer> s = Stream.generate(() -> 1);

因此,将findAny() 应用于此类流确实会始终短路并完成,而应用anyMatch(pred) 取决于谓词。然而,让我们过滤我们的无限流:

Stream<Integer> s = Stream.generate(() -> 1).filter(x -> x < 0);

结果流也是无限的吗?这是一个棘手的问题。它实际上不包含任何元素,但要确定这一点(例如,使用.iterator().hasNext()),我们必须检查无限数量的底层流元素,因此该操作永远不会完成。我也将这种流称为无限。但是,anyMatchfindAny 都不会使用这样的流:

Stream.generate(() -> 1).filter(x -> x < 0).anyMatch(x -> true);
Stream.generate(() -> 1).filter(x -> x < 0).findAny();

所以findAny()也不能保证完成,这取决于之前的中间流操作。

总之,我认为那篇博文非常具有误导性。在我看来,无限流行为在官方JavaDoc 中得到了更好的解释。

【讨论】:

不回答关于allMatch的问题。

以上是关于Java 8 流短路的主要内容,如果未能解决你的问题,请参考以下文章

HDU 5889 (最短路+网络流)

War(最短路+最大流)

HDU5889 Barricade(最短路)(网络流)

hdu-5889-最短路+网络流/最小割

Marriage Match IV(最短路+网络流)

[最短路,最大流最小割定理] 2019 Multi-University Training Contest 1 Path