在流中找到第一个<Optional<T>>

findFirst on a Stream<Optional<T>>

假设您有一个 Optional<T> 流,您想要在(如果存在)上触发 Consumer<T>

最优雅的处理方式是什么?

我可以通过 Optional::isPresent 过滤和 Optional::isGet 映射来管理,但这似乎 "hacky" 而不是 Optional 的精神:

Stream.of(a, b, c, d)
   .filter(Optional::isPresent)
   .map(Optional::get)
   .findFirst()
   .ifPresent(s -> System.out.println("This was cumbersome: " + s));

我同意这很麻烦,但它可能是最直接的解决方案,而且不会变得更加复杂。

我在类似情况下做过的一件事是创建一种方法将 Optional 变成 Stream:

private Stream<T> optionalStream(Optional<T> optional) {
    if (optional.isPresent())
        return Stream.of(optional.get());
    else
        return Stream.empty());
}

所以你可以使用 flatMap:

Stream.of(a, b, c, d)
    .flatMap(this::optionalStream).findFirst()...

当我第一次看到 Optional 时,我想知道为什么他们没有包含 stream 方法来执行此操作。更新:Java 9 将包含 Optional.

的这种方法

但老实说,我不确定这是否比您 filter 然后 map 的解决方案简单得多。

我不确定下面的任何一种方法是否比你的更好...

首先,您可以尝试 reduce():

Stream.of(a, b, c, d).reduce(Optional.empty(), (o1, o2) -> o1.isPresent() ? o1 : o2)
    .ifPresent(s -> System.out.println("This seems cumbersome as well: " + s));

您仍然会查询 Optional,尽管这会在减少流时发生。

一种更优雅的方法(尽管不那么冗长)是使用专门的 Consumer,它仅在非空时才接受 Optional 的值。为此,您需要按如下方式定义 OptionalConsumer

@FunctionalInterface
interface OptionalConsumer<T>
    extends Consumer<Optional<T>> {

    @Override
    default void accept(Optional<T> o) {
        if (o.isPresent()) {
            this.acceptIfPresent(o.get());
        }
    }

    void acceptIfPresent(T o);

    static <U> OptionalConsumer<U> of(Consumer<U> c) {
        return c::accept;
    }
}

您可以这样使用它:

Stream.of(a, b, c, d).forEach(OptionalConsumer.of(s -> System.out.println("Better? " + s)));

备注:

  1. 当您需要在许多不同的流中执行相同的逻辑时,也许这种方法更好。
  2. 我不敢打赌它在并行流上运行良好。
  3. 由于 OptionalConsumer 实际上是一个 Consumer,我不知道如何将它与 findFirst() 一起使用。

我认为 flatMap 在这里非常方便

 Stream.of(a, b, c, d)
   .flatMap(opt -> opt.map(Stream::of).orElse(Stream.empty()))
   .findFirst()
   .ifPresent(s -> System.out.println("This was cumbersome: " + s));

不理想,但至少简洁。