Aar*_*onC 3 java lazy-evaluation java-8 java-stream
我正在使用函数式编程风格来解决 Leetcode 的简单问题,计算一致字符串的数量。这个问题的前提很简单:计算谓词“所有值都在另一个集合中”成立的值的数量。
我有两种方法,一种我相当确定其行为符合我的要求,另一种我不太确定。两者都会产生正确的输出,但理想情况下,它们会在输出处于最终状态后停止评估其他元素。
public int countConsistentStrings(String allowed, String[] words) {
final Set<Character> set = allowed.chars()
.mapToObj(c -> (char)c)
.collect(Collectors.toCollection(HashSet::new));
return (int)Arrays.stream(words)
.filter(word ->
word.chars()
.allMatch(c -> set.contains((char)c))
)
.count();
}
Run Code Online (Sandbox Code Playgroud)
在此解决方案中,据我所知,allMatch 语句将在谓词不成立的 c 的第一个实例处终止并计算为 false,从而跳过该流中的其他值。
public int countConsistentStrings(String allowed, String[] words) {
Set<Character> set = allowed.chars()
.mapToObj(c -> (char)c)
.collect(Collectors.toCollection(HashSet::new));
return (int)Arrays.stream(words)
.filter(word ->
word.chars()
.mapToObj(c -> set.contains((char)c))
.reduce((a,b) -> a&&b)
.orElse(false)
)
.count();
}
Run Code Online (Sandbox Code Playgroud)
在此解决方案中,使用相同的逻辑,但allMatch我使用的map是 and then ,而不是reduce。从逻辑上讲,在单个false值来自map舞台后,reduce将始终评估为false。我知道 Java 流是惰性的,但我不确定它们什么时候“知道”它们到底有多惰性。这会比使用效率低吗allMatch,还是惰性会确保相同的操作?
最后,在这段代码中,我们可以看到 的值x将始终为 0,因为在仅过滤正数之后,它们的总和将始终为正(假设没有溢出),因此取正数的最小值和硬编码的 0 将为 0。流是否会足够懒惰,始终将其计算为 0,还是会减少过滤器之后的每个元素?
List<Integer> list = new ArrayList<>();
...
/*Some values added to list*/
...
int x = list.stream()
.filter(i -> i >= 0)
.reduce((a,b) -> Math.min(a+b, 0))
.orElse(0);
Run Code Online (Sandbox Code Playgroud)
综上所述,我们如何知道 Java 流何时会出现惰性?我在代码中看到了懒惰的机会,但是我如何保证我的代码尽可能懒惰呢?
您\xe2\x80\x99要求的实际术语是短路
\n\n\n此外,一些操作被视为短路操作。如果在提供无限输入时,中间操作可能会产生有限流,则该中间操作是短路的。如果当提供无限输入时,终端操作可以在有限时间内终止,则该终端操作是短路的。管道中的短路操作是无限流处理在有限时间内正常终止的必要条件,但不是充分条件。
\n
术语 \xe2\x80\x9clazy\xe2\x80\x9d 仅适用于中间操作,意味着它们仅在终端操作请求时才执行工作。情况总是如此,因此当您不链接终端操作时,任何中间操作都不会处理任何元素。
\n找出终端操作是否短路是相当容易的。转到APIStream文档并检查特定终端操作\xe2\x80\x99s文档是否包含这句话
\n\n\n这是短路端子操作。
\n
这并不意味着这种基于逻辑或代数的优化是不可能的。但责任在于 JVM\xe2\x80\x99s 优化器,它可能会对循环执行相同的操作。然而,这需要内联所有涉及的方法,以确保该条件始终适用并且没有必须保留的副作用。这种行为兼容性意味着即使处理得到优化,apeek(System.out::println)也会继续打印所有元素,就像它们已被处理一样。在实践中,您不应该期望这样的优化,因为 Stream 实现代码对于优化器来说太复杂了。