寻找一种链接选项的方法,以便返回第一个存在的选项。 如果不存在
Optional.empty()
应退回。
假设我有几种这样的方法:
Optional<String> find1()
我正在尝试将它们拴起来:
Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );
但是这当然行不通,因为
orElse
期望一个值,而 orElseGet
期望一个 Supplier
。
使用流:
Stream.of(find1(), find2(), find3())
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
如果您需要延迟评估查找方法,请使用供应商函数:
Stream.of(this::find1, this::find2, this::find3)
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
受 Sauli 的回答启发,可以使用
flatMap()
方法。
Stream.of(this::find1, this::find2, this::find3)
.map(Supplier::get)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
将Optional转换成Stream是很麻烦的。显然,这个问题将在 JDK9 中得到修复。所以这可以写成
Stream.of(this::find1, this::find2, this::find3)
.map(Supplier::get)
.flatMap(Optional::stream)
.findFirst();
虽然最初的问题是关于Java 8的,但是Java 9中引入了
。有了它,问题就可以解决如下
Optional<String> result = find1()
.or(this::find2)
.or(this::find3);
Optional<String> resultOpt = Optional.of(find1()
.orElseGet(() -> find2()
.orElseGet(() -> find3()
.orElseThrow(() -> new WhatEverException()))));
虽然我不确定它会提高可读性(IMO)。 Guava 提供了一种链接Optional的方法:
import com.google.common.base.Optional;
Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());
它可能是解决您的问题的另一种选择,但不使用 JDK 中的标准可选类。
如果你想保留标准API,你可以编写一个简单的实用方法:
static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
return first.isPresent() ? first : second;
}
然后:
Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));
如果您有很多链选项,也许最好使用其他已经提到的 Stream 方法。
result = find1()
.or(this::find2)
.or(this::find3);
Java 8
result = Optional.ofNullable(find1()
.orElse(find2()
.orElse(find3()
.orElse(null))));
性能:上述Java 8
解决方案每次都会预调用 find2() 和 find3(),即使(前)find1() 计算返回非空。 更新:具有惰性评估的性能最佳
Java 8解决方案(感谢@Alexander的评论)将是:
result = Optional.ofNullable(find1()
.orElseGet(() -> find2()
.orElseGet(() -> find3()
.orElse(null)))); // <-- null would be the last resort, when all findN are empty.
// #ofNullable (in the 1st line would wrap it to Optional
// and return Optional.empty() as OP requires.
orElse
s
的嵌套
String result = find1()
.map(Optional::of)
.orElseGet(Foo::find2())
.map(Optional::of)
.orElseGet(Foo::find3())
.orElseThrow(() -> new WhatEverException())
如果您想要
orElseThrow
作为结果,请删除
Optional<String>
。技巧是将 findX
返回的每个可选值包装到每个
orElseGet
之前的另一个可选值。or
。
如果提供您需要的数据的函数以纯文本形式返回,您可以这样做:Optional.ofNullable(supplier1())
.or(() -> Optional.ofNullable(supplier2()))
.or(() -> Optional.ofNullable(supplier3()))
.orElse(fallbackValue)
我经常静态导入
ofNullable
以简化可读性。如果需要,您还可以使用
orElse
或 orElseGet
关闭链条。现在,如果提供您需要的数据的函数已经返回由 Optional
包裹的数据,并且您不能或不想更改返回类型,您可以执行此技巧来启动链:
Optional.empty()
.or(supplier1)
.or(supplier2)
.orElse(fallbackValue)
ifPresentOrElse
find1().ifPresentOrElse( System.out::println, new Runnable() {
public void run() {
find2().ifPresentOrElse( System.out::println, new Runnable() {
public void run() {
find3().ifPresentOrElse( System.out::println, new Runnable() {
public void run() {
System.err.println( "nothing found…" );
}
} );
}
} );
}
} );
要利用
Optional
的价值做某事,您必须将
System.out::println
替换为您的 Consumer
(在此解决方案中也可以有不同的消费者)public static <T> T firstMatch(final Predicate<T> matcher, final T orElse, final T... values) {
for (T t : values) {
if (matcher.test(t)) {
return t;
}
}
return orElse;
}
然后你可以这样做:
public static <T> Optional<T> firstPresent(final Optional<T>... values) {
return firstMatch(Optional::isPresent, Optional.empty(), values);
}
首先转换流到可选使用两种方法之一
findAny() 或 findFirst()例如:
Stream s = Stream.of(1,2,3,4);s.findFirst().filter((a)->a+1).ifPresent(System.out::Println)
输出为:2
public <T> Optional<? extends T> firstOf(Optional<? extends T> first, @SuppressWarnings("unchecked") Supplier<Optional<? extends T>>... supp) {
if (first.isPresent()) return first;
for (Supplier<Optional <? extends T>> sup : supp) {
Optional<? extends T> opt = sup.get();
if (opt.isPresent()) {
return opt;
}
}
return Optional.empty();
}
public <T> Optional<? extends T> firstOf(Optional<? extends T> first, Stream<Supplier<Optional<? extends T>>> supp) {
if (first.isPresent()) return first;
Stream<Optional<? extends T>> present = supp.map(Supplier::get).filter(Optional::isPresent);
return present.findFirst().orElseGet(Optional::empty);
}
会的。
第一个迭代一系列供应商。返回第一个非空
Optional<>
。如果找不到,我们将返回一个空的
Optional
。第二个对遍历的
Stream
的
Suppliers
执行相同的操作,每个都(懒惰地)询问其值,然后过滤空 Optional
。返回第一个非空的,如果不存在,则返回空的。