我有以下函数,它试图逐步缩小输入集合,直到找到单个元素,即,当找到单个项目时,过滤应该停止,因为应用额外的过滤器可能导致根本不匹配。
public List<MyObject> determinePotentialCandidates(List<MyObject> allCandidates) {
List<MyObject> candidates = allCandidates.stream()
.filter(this::firstCondition)
.toList();
if (candidates.size() > 1) {
candidates = candidates.stream()
.filter(this::secondCondition)
.toList();
if (candidates.size() > 1) {
candidates = candidates.stream()
.filter(this::thirdCondition)
.collect(Collectors.toList());
}
// ... and so on
}
logResult(candidates);
return candidates;
}
由于每增加一个嵌套级别,这变得更难阅读,我想知道是否有更简洁的方法来写它。
最好的方法应该执行每个过滤步骤最多一次(尽管输入规模小和过滤便宜——这可能好多次执行相同的输入),包含一个出口点。
您可以将所有条件放入List
并对其进行循环,在每次迭代中应用一个过滤器,直到只剩下一个元素。
List<Predicate<MyObject>> conditions = List.of(this::firstCondition, this::secondCondition, this::thirdCondition /*...*/ );
for(int i = 0; i < conditions.size() && allCandidates.size() > 1; i++)
allCandidates = allCandidates.stream().filter(conditions.get(i)).toList();
// Note: you may need to check if the list is empty here
return allCandidates;
我看到一些选项:
在这两种情况下,我都假设事先知道条件。但这使解决方案保持刚性,不可扩展。从你发布的例子中,我不能说你是否在你最初的解决方案中观察到一些坚实的原则,例如打开/关闭。
选项:
- 多次应用
filter()
,串联。 - 如前所述,使用循环遍历条件列表。
对于这两种情况,我建议再做一些调整。
创建一个抽象类或接口来表示条件作为概念。创建另一个类,作为条件提供程序,此条件提供程序将在条件上维护一个列表。实例。条件提供程序可以稍后通过依赖注入或工厂与条件一起初始化。
你的类,在这个例子中,这个提供程序的客户端,将接收作为依赖(构造函数)注入的提供程序。您的类将向提供程序请求它必须验证的所有条件。您的类将在for循环中使用此条件列表来过滤集合。
这样,您可以灵活地使用哪些条件进行过滤。希望你的代码更容易维护。
希望这对你有帮助。