有没有一种优雅的方法来获取 Java 中多个方法返回的第一个非空值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26118090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there an elegant way to get the first non null value of multiple method returns in Java?
提问by cimnine
You have already seen this many times yourself, of that I'm sure:
你自己已经看过很多次了,我敢肯定:
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
}
We have different sources where an object we search could be. As a more vivid example we could image that we first check if a userid is in a list of privileged users. If not we check if the userid is in the list of allowed users. Else we return null. (It's not the best example but I hope it's a vivid-enough one.)
我们有不同的来源,我们搜索的对象可能在哪里。作为一个更生动的例子,我们可以想象我们首先检查用户 ID 是否在特权用户列表中。如果不是,我们检查用户 ID 是否在允许的用户列表中。否则我们返回null。(这不是最好的例子,但我希望它是一个足够生动的例子。)
Guavaoffers us some helpers that could beautify that code above:
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()
);
}
But, as the more experienced among us will have already seen, this may perform bad if the lookups (i.e. queryXXXXSource(args)
) take a certain amount of time. This is because we now query all sources first and then pass the results to the method that finds the first among those results which is not null
.
但是,正如我们中更有经验的人已经看到的那样,如果查找(即queryXXXXSource(args)
)需要一定的时间,这可能会很糟糕。这是因为我们现在首先查询所有源,然后将结果传递给在这些结果中找到第一个不是 的方法null
。
In contrast to the first example, where the next source is only evaluated when the former does not return something, this second solution may look better at first but could perform much worse.
与第一个示例相反,仅当前者不返回任何内容时才评估下一个源,第二个解决方案起初可能看起来更好,但性能可能更差。
Here's where we come to my actual question and to where I suggest something of that I hope someone has already implemented the base of it or of that someone might propose a even smarted solution.
这就是我们提出我的实际问题的地方,以及我提出的一些建议,我希望有人已经实现了它的基础,或者有人可能会提出一个更聪明的解决方案。
In plain English: Has someone already implemented such a defferedFirstNonNull
(see below) or something similar? Is there an easy plain-Java solution to achieve this with the new Streamframework? Can you propose another elegant solution that achieves the same result?
简单地说:有人已经实现了这样的defferedFirstNonNull
(见下文)或类似的东西吗?是否有一个简单的纯 Java 解决方案可以使用新的Stream框架来实现这一点?你能提出另一个达到相同结果的优雅解决方案吗?
Rules:Java 8 is allowed as well as active maintained and well-known third party libraries like Google's Guava or Apache's Commons Lang with Apache License or similar (No GPL!).
规则:允许使用 Java 8 以及积极维护和知名的第三方库,如 Google 的 Guava 或 Apache 的 Commons Lang 与 Apache 许可证或类似(无 GPL!)。
The proposed solution:
建议的解决方案:
public SomeObject findSomeObject(Arguments args) {
return Helper.deferredFirstNonNull(
Arrays.asList(
args -> queryFirstSource(args)
, args -> querySourceSource(args)
, args -> queryThirdSource(args)
)
, x -> x != null
)
}
So the method defferedFirstNonNull
would evaluate each lambda expression after another and as soon as the predicate (x -> x != null
) is true (i.e. we found a match) the method would return the result immediately and would not query any further source.
因此,该方法defferedFirstNonNull
将一个接一个地评估每个 lambda 表达式,一旦谓词 ( x -> x != null
) 为真(即我们找到匹配项),该方法将立即返回结果并且不会查询任何进一步的源。
PS: I know that the expressions args -> queryXXXXSource(args)
could be shortened to queryXXXXSource
. But that would render the proposed solution harder to read because it's not obvious on first sight what is going to happen.
PS:我知道表达式args -> queryXXXXSource(args)
可以缩短为queryXXXXSource
. 但这会使提议的解决方案更难阅读,因为乍一看会发生什么并不明显。
采纳答案by Holger
It depends on some factors you are not defining. Do you have a fixed, rather small set of query…Source
actions as shown in your question or are you rather heading to having a more flexible, extensible list of actions?
这取决于您未定义的某些因素。您是否有问题中所示的固定的、相当小的query…Source
操作集,或者您更倾向于拥有更灵活、可扩展的操作列表?
In the first case you might consider changing the query…Source
methods to return an Optional<SomeObject>
rather than SomeObject
or null
. If you change your methods to be like
在第一种情况下,您可能会考虑更改query…Source
方法以返回一个Optional<SomeObject>
而不是SomeObject
或null
。如果你改变你的方法就像
Optional<SomeObject> queryFirstSource(Arguments args) {
…
}
You can chain them this way:
你可以这样链接它们:
public SomeObject findSomeObject(Arguments args) {
return queryFirstSource(args).orElseGet(
()->querySecondSource(args).orElseGet(
()->queryThirdSource(args).orElse(null)));
}
If you can't change them or prefer them to return null
you can still use the Optional
class:
如果您无法更改它们或希望它们返回,null
您仍然可以使用Optional
该类:
public SomeObject findSomeObject(Arguments args) {
return Optional.ofNullable(queryFirstSource(args)).orElseGet(
()->Optional.ofNullable(querySecondSource(args)).orElseGet(
()->queryThirdSource(args)));
}
If you are looking for a more flexible way for a bigger number of possible queries, it is unavoidable to convert them to some kind of list or stream of Function
s. One possible solution is:
如果您正在寻找一种更灵活的方式来处理更多可能的查询,那么将它们转换为某种列表或Function
s流是不可避免的。一种可能的解决方案是:
public SomeObject findSomeObject(Arguments args) {
return Stream.<Function<Arguments,SomeObject>>of(
this::queryFirstSource, this::querySecondSource, this::queryThirdSource
).map(f->f.apply(args)).filter(Objects::nonNull).findFirst().orElse(null);
}
This performs the desired operation, however, it will compose the necessary action every time you invoke the method. If you want to invoke this method more often, you may consider composing an operation which you can re-use:
这将执行所需的操作,但是,每次调用该方法时,它将组成必要的操作。如果您想更频繁地调用此方法,您可以考虑编写一个可以重复使用的操作:
Function<Arguments, SomeObject> find = Stream.<Function<Arguments,SomeObject>>of(
this::queryFirstSource, this::querySecondSource, this::queryThirdSource
).reduce(a->null,(f,g)->a->Optional.ofNullable(f.apply(a)).orElseGet(()->g.apply(a)));
public SomeObject findSomeObject(Arguments args) {
return find.apply(args);
}
So you see, there are more than one way. And it depends on the actual task what direction to go. Sometimes, even the simple if
sequence might be appropriate.
所以你看,有不止一种方法。而这取决于实际任务要往什么方向发展。有时,即使是简单的if
顺序也可能是合适的。
回答by m3th0dman
Yes, there is:
就在这里:
Arrays.asList(source1, source2, ...)
.stream()
.filter(s -> s != null)
.findFirst();
This is more flexible, since it returns an Optional
not null
in case a not-null source
is found.
这更灵活,因为如果发现非空,它会返回一个Optional
not 。null
source
Edit: If you want lazy evaluation you should use a Supplier
:
编辑:如果你想要懒惰的评估,你应该使用Supplier
:
Arrays.<Supplier<Source>>asList(sourceFactory::getSource1, sourceFactory::getSource2, ...)
.stream()
.filter(s -> s.get() != null)
.findFirst();
回答by assylias
I would write it like this (you may not need generics here but why not do it):
我会这样写(你可能不需要泛型,但为什么不这样做):
public static <A, T> Optional<T> findFirst(Predicate<T> predicate, A argument,
Function<A, T>... functions) {
return Arrays.stream(functions)
.map(f -> f.apply(argument))
.filter(predicate::test)
.findFirst();
}
And you can call it with:
您可以使用以下命令调用它:
return findFirst(Objects::nonNull, args, this::queryFirstSource,
this::querySecondSource,
this::queryThirdSource);
(assuming your queryXXX
methods are instance methods)
(假设您的queryXXX
方法是实例方法)
The methods will be applied in order until one returns a value that matches the predicate (in the example above: returns a non null value).
这些方法将按顺序应用,直到返回与谓词匹配的值(在上面的示例中:返回非空值)。