【发布时间】:2014-11-24 22:04:27
【问题描述】:
你自己已经看过很多次了,我敢肯定:
public SomeObject findSomeObject(Arguments args) {
SomeObject so = queryFirstSource(args); // the most likely source first, hopefully
if (so != null) return so;
so = querySecondSource(args); // a source less likely than the first, hopefully
if (so != null) return so;
so = queryThirdSource(args); // a source less likely than the previous, hopefully
if (so != null) return so;
// and so on
}
我们有不同的来源,我们搜索的对象可能在哪里。作为一个更生动的例子,我们可以想象我们首先检查用户 ID 是否在特权用户列表中。如果不是,我们检查用户 ID 是否在允许的用户列表中。否则我们返回 null。 (这不是最好的例子,但我希望它是一个足够生动的例子。)
Guava 为我们提供了一些可以美化上面代码的助手:
public SomeObject findSomeObject(Arguments args) {
// if there are only two objects
return com.google.common.base.Objects.firstNonNull(queryFirstSource(args), querySecondSource(args));
// or else
return com.google.common.collect.Iterables.find(
Arrays.asList(
queryFirstSource(args)
, querySecondSource(args)
, queryThirdSource(args)
// , ...
)
, com.google.common.base.Predicates.notNull()
);
}
但是,正如我们当中更有经验的人已经看到的那样,如果查找(即queryXXXXSource(args))需要一定的时间,这可能会表现不佳。这是因为我们现在首先查询所有源,然后将结果传递给在那些结果中找到第一个不是 null 的方法。
与第一个示例相比,下一个源仅在前一个不返回某些内容时才评估,第二个解决方案一开始可能看起来更好,但性能可能更差。
这是我们提出实际问题的地方,也是我提出一些建议的地方,我希望有人已经实现了它的基础,或者有人可能会提出一个更聪明的解决方案。
用简单的英语:有人已经实现了这样的defferedFirstNonNull(见下文)或类似的东西吗?使用新的Stream 框架是否有一个简单的纯Java 解决方案来实现这一点?你能提出另一个达到相同结果的优雅解决方案吗?
规则: 允许使用 Java 8 以及积极维护和知名的第三方库,例如具有 Apache 许可证或类似许可证(无 GPL!)的 Google 的 Guava 或 Apache 的 Commons Lang。
建议的解决方案:
public SomeObject findSomeObject(Arguments args) {
return Helper.deferredFirstNonNull(
Arrays.asList(
args -> queryFirstSource(args)
, args -> querySourceSource(args)
, args -> queryThirdSource(args)
)
, x -> x != null
)
}
因此,defferedFirstNonNull 方法将逐个评估每个 lambda 表达式,并且一旦谓词 (x -> x != null) 为真(即我们找到匹配项),该方法将立即返回结果并且不会查询任何进一步的源.
PS:我知道表达式args -> queryXXXXSource(args) 可以缩短为queryXXXXSource。但这会使提议的解决方案更难阅读,因为乍一看并不明显会发生什么。
【问题讨论】: