将 Java 8 的 Optional 与 Stream::flatMap 一起使用

2022-01-22 00:00:00 lambda java-8 java java-stream

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

The new Java 8 stream framework and friends make for some very concise java code, but I have come across a seemingly-simple situation that is tricky to do concisely.

考虑一个List<Thing>事物和方法可选的<Other>解决(事物事物).我想将 Thing 映射到 Optional 并获取第一个 Other.显而易见的解决方案是使用 things.stream().flatMap(this::resolve).findFirst(),但 flatMap 要求您返回一个流,并且 Optional 没有 stream() 方法(或者它是 Collection 或提供将其转换为或将其视为 <代码>集合).

Consider a List<Thing> things and method Optional<Other> resolve(Thing thing). I want to map the Things to Optional<Other>s and get the first Other. The obvious solution would be to use things.stream().flatMap(this::resolve).findFirst(), but flatMap requires that you return a stream, and Optional doesn't have a stream() method (or is it a Collection or provide a method to convert it to or view it as a Collection).

我能想到的最好的是:

things.stream()
    .map(this::resolve)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

但对于看似非常常见的情况而言,这似乎非常冗长.谁有更好的主意?

But that seems awfully long-winded for what seems like a very common case. Anyone have a better idea?

推荐答案

Java 9

Optional.stream 已添加到 JDK 9.这使您无需任何辅助方法即可执行以下操作:

Java 9

Optional.stream has been added to JDK 9. This enables you to do the following, without the need of any helper method:

Optional<Other> result =
    things.stream()
          .map(this::resolve)
          .flatMap(Optional::stream)
          .findFirst();

Java 8

是的,这是 API 中的一个小漏洞,因为将 Optional<T> 变成零或一长度 Stream<T> 有点不方便代码>.你可以这样做:

Java 8

Yes, this was a small hole in the API, in that it's somewhat inconvenient to turn an Optional<T> into a zero-or-one length Stream<T>. You could do this:

Optional<Other> result =
    things.stream()
          .map(this::resolve)
          .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
          .findFirst();

flatMap 中使用三元运算符有点麻烦,因此最好编写一个辅助函数来执行此操作:

Having the ternary operator inside the flatMap is a bit cumbersome, though, so it might be better to write a little helper function to do this:

/**
 * Turns an Optional<T> into a Stream<T> of length zero or one depending upon
 * whether a value is present.
 */
static <T> Stream<T> streamopt(Optional<T> opt) {
    if (opt.isPresent())
        return Stream.of(opt.get());
    else
        return Stream.empty();
}

Optional<Other> result =
    things.stream()
          .flatMap(t -> streamopt(resolve(t)))
          .findFirst();

在这里,我已经内联了对 resolve() 的调用,而不是单独的 map() 操作,但这是个人喜好问题.

Here, I've inlined the call to resolve() instead of having a separate map() operation, but this is a matter of taste.

相关文章