返回Java流中的第一个结果匹配谓词或所有不匹配的结果

Rik*_*Rik 12 java aggregate predicate java-8 java-stream

我有一个Validator提供isValid(Thing)方法的接口,返回ValidationResult包含a boolean和原因消息的方法.

我想创建一个ValidatorAggregator这个接口的实现,它在多个Validators上执行OR (如果有的话Validator返回一个肯定的结果,那么结果是正的).如果任何验证器成功,我想短路并返回其结果.如果没有验证器成功,我想返回所有失败消息.

我可以使用流简洁地执行此操作findFirst().orElse(...)但是如果findFirst返回空,则使用此模式会丢失所有中间结果:

public ValidationResult isValid(final Thing thing) {
    return validators.stream()
      .map(v -> validator.isValid(thing))
      .filter(ValidationResult::isValid)
      .findFirst()
      .orElseGet(() -> new ValidationResult(false, "All validators failed'));
}
Run Code Online (Sandbox Code Playgroud)

有没有办法使用流捕获失败的结果,或者实际上比下面更简洁?

public ValidationResult isValid(final Thing thing) {
    final Set<ValidationResult> failedResults = new HashSet<>();
    for (Validator validator : validators) {
        final ValidationResult result = validator.isValid(thing);
        if (result.isValid()) {
            return result;
        }
        failedResults.add(result);
    }
    return new ValidationResult(false, "No successful validator: " + failedResults); 
    // (assume failedResults stringifies nicely)
}
Run Code Online (Sandbox Code Playgroud)

编辑:基于评论,我同意我想要做的是过早优化(特别是因为这些验证器非常轻量级).我可能会采用类似于Holger计算所有验证和分区成功/不成功结果的解决方案.

这被标记为可以将流分成两个流的欺骗partitioningBy答案就是这样,但我认为这个问题是在讨论和讨论,这是一个不同的问题.

Hol*_*ger 3

不存在能够以相同效率处理所有情况的完美解决方案。即使您的循环变体满足短路并仅处理验证器一次的标准,也存在创建和填充集合的缺点,如果只有一次验证成功,则可能会发现没有必要。

\n\n

选择取决于与操作相关的实际成本以及至少一次成功验证的可能性。如果以最佳性能处理常见情况,它可能会超过处理不常见情况时解决方案\xe2\x80\x99s 的惩罚。

\n\n

所以

\n\n
// you may use this if the likelihood of a success is high; assumes\n// reasonable costs for the validation and consists (repeatable) results\npublic ValidationResult isValid(final Thing thing) {\n    return validators.stream()\n      .map(v -> v.isValid(thing))\n      .filter(ValidationResult::isValid)\n      .findFirst()\n      .orElseGet(() -> new ValidationResult(false, "All validators failed"\n        + validators.stream().map(v -> v.isValid(thing)).collect(Collectors.toSet())));\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n
// you may use this if the likelihood of a success is\n// very low and/or you intent to utilize parallel processing\npublic ValidationResult isValid(final Thing thing) {\n    Map<Boolean,Set<ValidationResult>> results = validators.stream()\n        .map(v -> v.isValid(thing))\n        .collect(Collectors.partitioningBy(ValidationResult::isValid, Collectors.toSet()));\n    return results.get(true).stream().findAny()\n        .orElseGet(() -> new ValidationResult(false,\n                             "No successful validator: "+results.get(false)));\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n
// if chances of successful validation are mixed or unpredictable\n// or validation is so expensive that everything else doesn\'t matter\n// stay with the loop\npublic ValidationResult isValid(final Thing thing) {\n    final Set<ValidationResult> failedResults = new HashSet<>();\n    for (Validator validator : validators) {\n        final ValidationResult result = validator.isValid(thing);\n        if (result.isValid()) {\n            return result;\n        }\n        failedResults.add(result);\n    }\n    return new ValidationResult(false, "No successful validator: " + failedResults);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

考虑对列表进行排序,以便成功机会较高的验证者位于开头\xe2\x80\xa6

\n