在Stream :: flatMap中使用Java 8的Optional

新的Java 8流框架和朋友们制作了一些非常简洁的Java代码,但是我遇到了一个看似简单的情况,简单易懂。


考虑一个List<Thing> things方法Optional<Other> resolve(Thing thing)。我想将Things 映射到Optional<Other>s并获得第一个Other。显而易见的解决方案是使用things.stream().flatMap(this::resolve).findFirst(),但flatMap要求您返回一个流,并且Optional没有stream()方法(或者它是Collection一个方法或提供将其转换为或以其方式查看的方法Collection)。


我能想到的最好的是:


things.stream()

    .map(this::resolve)

    .filter(Optional::isPresent)

    .map(Optional::get)

    .findFirst();

但这似乎是一个非常普遍的案例,似乎非常冗长。谁有更好的主意?


森栏
浏览 1310回答 3
3回答

白板的微信

Java 9Optional.stream 已添加到JDK 9.这使您可以执行以下操作,而无需任何帮助方法:Optional<Other> result =&nbsp; &nbsp; things.stream()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .map(this::resolve)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .flatMap(Optional::stream)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .findFirst();Java 8是的,这是API中的一个小漏洞,因为将Optional变为零或一个长度的流有点不方便。你可以这样做:Optional<Other> result =&nbsp; &nbsp; things.stream()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .map(this::resolve)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .findFirst();但是在flatMap中使用三元运算符有点麻烦,所以编写一个小辅助函数来执行此操作可能会更好:/**&nbsp;* Turns an Optional<T> into a Stream<T> of length zero or one depending upon&nbsp;* whether a value is present.&nbsp;*/static <T> Stream<T> streamopt(Optional<T> opt) {&nbsp; &nbsp; if (opt.isPresent())&nbsp; &nbsp; &nbsp; &nbsp; return Stream.of(opt.get());&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; return Stream.empty();}Optional<Other> result =&nbsp; &nbsp; things.stream()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .flatMap(t -> streamopt(resolve(t)))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .findFirst();在这里,我已经内联调用resolve()而不是单独的map()操作,但这是一个品味问题。

慕沐林林

你不能像你已经做的那样更简洁。你声称自己不想.filter(Optional::isPresent) 和 .map(Optional::get)。这已经通过@StuartMarks描述的方法解决了,但是结果你现在将它映射到一个Optional<T>,所以现在你需要使用.flatMap(this::streamopt)和a get()到底。所以它仍然包含两个语句,现在您可以使用新方法获得异常!因为,如果每个可选项都是空的怎么办?然后findFirst()将返回一个空的可选项,你的get()意志将失败!那么你有什么:things.stream()&nbsp; &nbsp; .map(this::resolve)&nbsp; &nbsp; .filter(Optional::isPresent)&nbsp; &nbsp; .map(Optional::get)&nbsp; &nbsp; .findFirst();是真正实现你想要什么是最好的办法,那就是你要的结果保存为一个T,而不是一个Optional<T>。我冒昧地创建了一个CustomOptional<T>包装的类,Optional<T>并提供了一个额外的方法,flatStream()。请注意,您无法扩展Optional<T>:class CustomOptional<T> {&nbsp; &nbsp; private final Optional<T> optional;&nbsp; &nbsp; private CustomOptional() {&nbsp; &nbsp; &nbsp; &nbsp; this.optional = Optional.empty();&nbsp; &nbsp; }&nbsp; &nbsp; private CustomOptional(final T value) {&nbsp; &nbsp; &nbsp; &nbsp; this.optional = Optional.of(value);&nbsp; &nbsp; }&nbsp; &nbsp; private CustomOptional(final Optional<T> optional) {&nbsp; &nbsp; &nbsp; &nbsp; this.optional = optional;&nbsp; &nbsp; }&nbsp; &nbsp; public Optional<T> getOptional() {&nbsp; &nbsp; &nbsp; &nbsp; return optional;&nbsp; &nbsp; }&nbsp; &nbsp; public static <T> CustomOptional<T> empty() {&nbsp; &nbsp; &nbsp; &nbsp; return new CustomOptional<>();&nbsp; &nbsp; }&nbsp; &nbsp; public static <T> CustomOptional<T> of(final T value) {&nbsp; &nbsp; &nbsp; &nbsp; return new CustomOptional<>(value);&nbsp; &nbsp; }&nbsp; &nbsp; public static <T> CustomOptional<T> ofNullable(final T value) {&nbsp; &nbsp; &nbsp; &nbsp; return (value == null) ? empty() : of(value);&nbsp; &nbsp; }&nbsp; &nbsp; public T get() {&nbsp; &nbsp; &nbsp; &nbsp; return optional.get();&nbsp; &nbsp; }&nbsp; &nbsp; public boolean isPresent() {&nbsp; &nbsp; &nbsp; &nbsp; return optional.isPresent();&nbsp; &nbsp; }&nbsp; &nbsp; public void ifPresent(final Consumer<? super T> consumer) {&nbsp; &nbsp; &nbsp; &nbsp; optional.ifPresent(consumer);&nbsp; &nbsp; }&nbsp; &nbsp; public CustomOptional<T> filter(final Predicate<? super T> predicate) {&nbsp; &nbsp; &nbsp; &nbsp; return new CustomOptional<>(optional.filter(predicate));&nbsp; &nbsp; }&nbsp; &nbsp; public <U> CustomOptional<U> map(final Function<? super T, ? extends U> mapper) {&nbsp; &nbsp; &nbsp; &nbsp; return new CustomOptional<>(optional.map(mapper));&nbsp; &nbsp; }&nbsp; &nbsp; public <U> CustomOptional<U> flatMap(final Function<? super T, ? extends CustomOptional<U>> mapper) {&nbsp; &nbsp; &nbsp; &nbsp; return new CustomOptional<>(optional.flatMap(mapper.andThen(cu -> cu.getOptional())));&nbsp; &nbsp; }&nbsp; &nbsp; public T orElse(final T other) {&nbsp; &nbsp; &nbsp; &nbsp; return optional.orElse(other);&nbsp; &nbsp; }&nbsp; &nbsp; public T orElseGet(final Supplier<? extends T> other) {&nbsp; &nbsp; &nbsp; &nbsp; return optional.orElseGet(other);&nbsp; &nbsp; }&nbsp; &nbsp; public <X extends Throwable> T orElseThrow(final Supplier<? extends X> exceptionSuppier) throws X {&nbsp; &nbsp; &nbsp; &nbsp; return optional.orElseThrow(exceptionSuppier);&nbsp; &nbsp; }&nbsp; &nbsp; public Stream<T> flatStream() {&nbsp; &nbsp; &nbsp; &nbsp; if (!optional.isPresent()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Stream.empty();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return Stream.of(get());&nbsp; &nbsp; }&nbsp; &nbsp; public T getTOrNull() {&nbsp; &nbsp; &nbsp; &nbsp; if (!optional.isPresent()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return get();&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public boolean equals(final Object obj) {&nbsp; &nbsp; &nbsp; &nbsp; return optional.equals(obj);&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public int hashCode() {&nbsp; &nbsp; &nbsp; &nbsp; return optional.hashCode();&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public String toString() {&nbsp; &nbsp; &nbsp; &nbsp; return optional.toString();&nbsp; &nbsp; }}你会看到我添加了flatStream(),如下所示:public Stream<T> flatStream() {&nbsp; &nbsp; if (!optional.isPresent()) {&nbsp; &nbsp; &nbsp; &nbsp; return Stream.empty();&nbsp; &nbsp; }&nbsp; &nbsp; return Stream.of(get());}用作:String result = Stream.of("a", "b", "c", "de", "fg", "hij")&nbsp; &nbsp; &nbsp; &nbsp; .map(this::resolve)&nbsp; &nbsp; &nbsp; &nbsp; .flatMap(CustomOptional::flatStream)&nbsp; &nbsp; &nbsp; &nbsp; .findFirst()&nbsp; &nbsp; &nbsp; &nbsp; .get();你还需要返回Stream<T>这里,因为你不能返回T,因为如果!optional.isPresent(),那么T == null如果你声明它这样的,但那么你.flatMap(CustomOptional::flatStream)会尝试添加null到流,这是不可能的。例如:public T getTOrNull() {&nbsp; &nbsp; if (!optional.isPresent()) {&nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; }&nbsp; &nbsp; return get();}用作:String result = Stream.of("a", "b", "c", "de", "fg", "hij")&nbsp; &nbsp; &nbsp; &nbsp; .map(this::resolve)&nbsp; &nbsp; &nbsp; &nbsp; .map(CustomOptional::getTOrNull)&nbsp; &nbsp; &nbsp; &nbsp; .findFirst()&nbsp; &nbsp; &nbsp; &nbsp; .get();现在将抛出一个NullPointerException内部流操作。结论您使用的方法实际上是最好的方法。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java