Return Java 流中的第一个结果匹配谓词或所有非匹配结果
Return first result matching predicate in a Java stream or all non-matching results
我有一个 Validator
接口,它提供了一个 isValid(Thing)
方法,returning 一个 ValidationResult
其中包含一个 boolean
和一个原因消息。
我想创建此接口的 ValidatorAggregator
实现,它在多个 Validator
之间执行 OR(如果任何 Validator
return 是肯定的结果,那么结果为阳性)。如果任何验证器成功,我想短路并只 return 它的结果。如果没有验证器成功,我想 return 所有失败消息。
我可以使用流和 findFirst().orElse(...)
简洁地完成此操作,但是如果 findFirst
return 为空,则使用此模式我将丢失所有中间结果:
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)
}
编辑:根据评论,我同意我正在尝试做的是过早的优化(特别是因为这些验证器非常轻量级)。我可能会采用类似于 Holger 的计算所有验证并划分为 successful/unsuccessful 结果的解决方案。
这被标记为 Can you split a stream into two streams? 的骗局,partitioningBy
的答案有点像,但我认为这个问题是在问,而讨论是在回答,一个不同的问题。
没有以相同效率处理所有情况的完美解决方案。即使是满足 short-circuiting 标准并且只处理一次验证器的循环变体,也有创建和填充一个集合的缺点,如果只有一个验证成功,这可能是不必要的。
选择取决于与操作相关的实际成本以及至少进行一次成功验证的可能性。如果以最佳性能处理常见情况,它可能会超过解决方案对处理不常见情况的惩罚。
所以
// 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);
}
考虑对列表进行排序,使成功机会较高的验证者排在开头……
我有一个 Validator
接口,它提供了一个 isValid(Thing)
方法,returning 一个 ValidationResult
其中包含一个 boolean
和一个原因消息。
我想创建此接口的 ValidatorAggregator
实现,它在多个 Validator
之间执行 OR(如果任何 Validator
return 是肯定的结果,那么结果为阳性)。如果任何验证器成功,我想短路并只 return 它的结果。如果没有验证器成功,我想 return 所有失败消息。
我可以使用流和 findFirst().orElse(...)
简洁地完成此操作,但是如果 findFirst
return 为空,则使用此模式我将丢失所有中间结果:
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)
}
编辑:根据评论,我同意我正在尝试做的是过早的优化(特别是因为这些验证器非常轻量级)。我可能会采用类似于 Holger 的计算所有验证并划分为 successful/unsuccessful 结果的解决方案。
这被标记为 Can you split a stream into two streams? 的骗局,partitioningBy
的答案有点像,但我认为这个问题是在问,而讨论是在回答,一个不同的问题。
没有以相同效率处理所有情况的完美解决方案。即使是满足 short-circuiting 标准并且只处理一次验证器的循环变体,也有创建和填充一个集合的缺点,如果只有一个验证成功,这可能是不必要的。
选择取决于与操作相关的实际成本以及至少进行一次成功验证的可能性。如果以最佳性能处理常见情况,它可能会超过解决方案对处理不常见情况的惩罚。
所以
// 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);
}
考虑对列表进行排序,使成功机会较高的验证者排在开头……