在 Java 8 中懒惰地返回第一个非空列表
Posted
技术标签:
【中文标题】在 Java 8 中懒惰地返回第一个非空列表【英文标题】:Return first non empty list lazyily in Java 8 【发布时间】:2017-07-13 08:07:33 【问题描述】:我有 N 个从存储库返回数据的列表。我想返回这三个列表中的第一个非空列表(每个列表执行不同的 SQL 来获取数据)。
问题是我想懒惰地这样做,这样如果我已经找到了可接受的结果,我就不需要在数据库上执行 SQL。我的代码是(修改)
@Override
public List<Something> dataService(Data data)
return firstNonEmptyList(repository.getDataWayOne(data.getParameter()),
repository.getDataWayTwo(data.getParameter()),
repository.getDataWayThree(data.getParameter().getAcessoryParameter())
Collections.singletonList(repository.getDefaultData(data.getParameter()));
@SafeVarargs
private final List<Something> firstNonEmptyList(List<Something>... lists)
for (List<Something> list : lists)
if (!list.isEmpty())
return list;
return null;
这行得通,但它并不懒惰。有什么想法吗?
【问题讨论】:
如果我错了,请纠正我,但我认为如果将列表调用包装在 lambdas 中会很懒惰。在firstNonEmptyList
中,您将评估每个 lambda,直到找到您想要的那个(即返回非空列表的那个)。未被调用的 lambda 永远不会执行查询。无论如何,shmosel 提供的解决方案符合我的描述并且更加优雅。
【参考方案1】:
您可以创建一个供应商流并按照遇到的顺序对其进行评估,直到找到结果:
return Stream.<Supplier<List<Something>>>of(
() -> repository.getDataWayOne(data.getParameter()),
() -> repository.getDataWayTwo(data.getParameter()),
() -> repository.getDataWayThree(data.getParameter().getAcessoryParameter()),
() -> Collections.singletonList(repository.getDefaultData(data.getParameter()))
)
.map(Supplier::get)
.filter(l -> !l.isEmpty())
.findFirst()
.orElse(null);
每个供应商都定义了如何访问结果集,而不是在执行map()
之前实际尝试它。由于filter()
和map()
是stateless 操作,因此将调用每个供应商并在尝试下一个供应商之前验证其结果。如果找到非空结果,则流将立即终止,因为findFirst()
是short-circuiting。
【讨论】:
这是让一些人头晕目眩的 Java 8 答案之一。如果只能一步一步解释,为什么是懒加载? 这是一个很好的答案,最Java8-ic的方式,原谅我的法语! @GrzegorzGórkiewicz 我试过了。希望现在更清楚一点。 @shmosel 可以与...filter(List::isEmpty)
一起使用。为了保持一致性,因为您正在映射 Supplier::get
?
这是完美的,就像一个魅力。通过了所有测试,它很懒惰。加上解释后我明白了。【参考方案2】:
如果流不是您的菜,您仍然可以使用 lambdas 来实现您想要的,只需对原始代码稍作修改。
public List<Something> dataService(Data data)
return firstNonEmptyList(
() -> repository.getDataWayOne(data.getParameter()),
() -> repository.getDataWayTwo(data.getParameter()),
() -> repository.getDataWayThree(data.getParameter().getAcessoryParameter()),
() -> Collections.singletonList(repository.getDefaultData(data.getParameter()))
);
private final List<Something> firstNonEmptyList(Supplier<List<Something>>... listSuppliers)
for (Supplier<List<Something>> supplier : listSuppliers)
List<Something> list = supplier.get();
if (!list.isEmpty())
return list;
return null;
【讨论】:
巧合,我的最后一条评论正是你在这里所做的。它有效(我猜),但问题是它隐藏了懒惰的意图。在这种情况下,欢迎使用更实用的样式。 我实际上认为恰恰相反,firstNonEmptyList
的短路行为很明显。对于流,只有在您非常熟悉流 API 的情况下才很明显,但并不是每个人都如此。另外,该方法上的 Javadocs 可用于帮助使其更加明显。
这也有效并通过了我的所有测试,包括延迟加载。我很感激。我将使用 shmosel 的,因为它消除了辅助方法。以上是关于在 Java 8 中懒惰地返回第一个非空列表的主要内容,如果未能解决你的问题,请参考以下文章
Leetcode练习(python):第414题:第三大的数:给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。
Leetcode练习(python):第414题:第三大的数:给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。