如何使用 Stream.allMatch() 对空流返回 false?

use*_*157 5 java java-stream

我想使用Stream.allMatch(),但当false流为空时我需要 a 。

public static void main (String[] args) throws java.lang.Exception
{
   System.out.println(testMethod(Stream.empty())); // <- expected false (but is true)
   System.out.println(testMethod(Stream.of("match", "match"))); // <- expected true
   System.out.println(testMethod(Stream.of("match", "no match"))); // <- expected false
}
    
private static boolean testMethod(Stream<String> stream) {
   return stream.allMatch(text -> "match".equals(text));
}
Run Code Online (Sandbox Code Playgroud)

https://ideone.com/8Nsjiw

我不想用...

我想,我必须使用noMatch(),但我没有得到正确工作的否定。

private static boolean testMethod(Stream<String> stream) {
   // my guess, but the results are wrong
   return !stream.noneMatch(text -> !"match".equals(text));
}
Run Code Online (Sandbox Code Playgroud)

这与How to return false for an empty list if using Stream.allMatch()?不重复。因为我使用Streams 而不是Lists。


也许是 XY 问题,所以这是我的上限用例

这是关于状态收集的。让我们简单地将其作为用户的下载状态指示器。(不是我真正的用例,但很接近)。我有一张看起来像这样的
桌子downloads

用户身份 下载名称 下载状态 数据
1 下载1 加载中 无效的
2 下载2 准备好 0x4545475

我无法更改表格,而且我的能力受到限制,因为这是一个巨大的项目(遗憾的是不可能重构世界)。
不管怎样,我想向用户展示一个关于他的下载的指标。仅当所有下载都准备就绪时,该指示器才应可见。但是,如果尚未下载,则用户不应看到该指示器。

我有一个给定的存储库方法Stream<Downloads> getDownloadsForUser()。短路对于减少负载很重要。

k31*_*159 4

Java 的Stream设计并不容易在一次传递中检查流是否为空以及所有元素是否与给定谓词匹配。您可以使用局部变量来跟踪计数(但它必须是有效的最终变量,因此您必须使用众所周知的技巧,例如 1 元素数组)。这样,您可以保留 Stream 的短路行为,同时检查流是否为空。

然而,在纯数学/逻辑术语中,将空流视为不具有全部与谓词匹配的元素是没有意义的。这就是空性真理概念。

/**
 * DO NOT USE. This uses the wrong logic: see
 * <a href="https://en.wikipedia.org/wiki/Vacuous_truth#In_computer_programming">vacuous truth</a>.
 */
private static boolean allMatchExceptReturnFalseIfEmpty(Stream<String> stream) {
    boolean[] empty = { true };
    return stream.allMatch(text -> { empty[0] = false; return "match".equals(text); }) && !empty[0];
}
Run Code Online (Sandbox Code Playgroud)

上述方法将空流视为返回相反结果的特殊值。在许多情况下,最好在这种情况下采取一些特殊处理,如果方法抛出异常,则可以这样做:

/**
 * Better. Checks that all elements match, at the same time checking that the stream is not empty.
 */
private static boolean allMatchNonEmptyStream(Stream<String> stream) {
    boolean[] empty = { true };
    boolean result = stream.allMatch(text -> { empty[0] = false; return "match".equals(text); });
    if (empty[0])
        throw new IllegalArgumentException("Stream must not be empty");
    return result;
}
Run Code Online (Sandbox Code Playgroud)

看待这个问题的另一种方式是你想要一个三态结果:

enum MatchResult { EMPTY, ALL_MATCH, SOME_DONT_MATCH }

private static MatchResult allMatchThreeState(Stream<String> stream) {
    boolean[] empty = { true };
    boolean allMatch = stream.allMatch(text -> { empty[0] = false; return "match".equals(text); });
    return (empty[0]) ? MatchResult.EMPTY : allMatch ? MatchResult.ALL_MATCH : MatchResult.SOME_DONT_MATCH;
    
}
Run Code Online (Sandbox Code Playgroud)

在您的情况下(在更新您的问题之后),您可能希望利用此三态值来显示,例如,(1)没有指示器;(2)“下载中”;(3)“下载完成”。即使您现在不想拥有 3 种不同的指示器状态,这种 3 状态方法结果也会为您提供更好的灵活性。