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

标签 java java-8 java-stream aggregate predicate

我有一个 Validator 接口(interface),它提供了一个 isValid(Thing) 方法,返回一个 ValidationResult 其中包含一个 boolean 和原因消息。

我想创建此接口(interface)的 ValidatorAggregator 实现,它在多个 Validator 之间执行 OR(如果任何 Validator 返回肯定结果,则结果为正)。如果任何 validator 成功,我想短路并返回其结果。如果没有 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'));
}

有没有什么方法可以使用流捕获失败的结果,或者确实比下面的方法更简洁?

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)
}

编辑:根据评论,我同意我正在尝试做的是过早的优化(特别是因为这些 validator 非常轻量级)。我可能会采用类似于 Holger 的计算所有验证并将其划分为成功/不成功结果的解决方案。

这被标记为 Can you split a stream into two streams? 的骗局partitioningBy 的答案有点像,但我认为这个问题是在问,而讨论是在回答一个不同的问题。

最佳答案

没有完美的解决方案可以以相同的效率处理所有情况。即使您的循环变体满足了短路和只处理 validator 一次的标准,也有创建和填充集合的缺点,如果只有一个验证成功,这可能是不必要的。

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

所以

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

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

关于java - 返回 Java 流中的第一个结果匹配谓词或所有非匹配结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48997935/

相关文章:

java - 如何使自定义布局的背景变暗?

java - 在映射中查找枚举值以初始化列表

java - 快速获取java中两个浮点列表之和的max和maxIndex

java - 将多个列表的每个元素合并为一个元素并返回java 8中的另一个列表

Java FX 模块化应用程序,未找到模块(Java 11,Intellij)

java - 我如何更改此方法以获取字符串而不是整数

java - 从 CompletableFuture 抛出异常

java - 使用 Stream 避免 NoSuchElementException

java - Jooq 在 sqlite 上存储后不返回主键

java - java 8 Stream API 中的分组依据