1. ホーム

[解決済み】Java 8のOptionalをStream::flatMapで使用する。

2022-04-03 02:59:50

質問

新しいJava 8のストリームフレームワークとその仲間たちは、非常に簡潔なJavaコードを作ってくれますが、一見単純に見える状況でも、簡潔に行うには厄介なことに遭遇しています。

を考えてみましょう。 List<Thing> things とメソッド Optional<Other> resolve(Thing thing) . をマッピングしたいのですが ThingOptional<Other> を取得し、最初の Other . 明らかな解決策は things.stream().flatMap(this::resolve).findFirst() しかし flatMap はストリームを返すことが必要であり Optional には stream() メソッド(あるいは Collection に変換する、あるいはそれを表示するメソッドを提供する。 Collection ).

私が思いつくのは、これくらいです。

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

しかし、ごく一般的なケースと思われるのに、やけに長文に感じます。どなたか良いアイデアをお持ちの方はいらっしゃいますか?

解決方法は?

ジャバ9

Optional.stream が JDK 9 で追加されました。 これにより、ヘルパーメソッドを使わずに、以下のようなことができるようになりました。

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

ジャバ8

そう、これは API の小さな穴だった。 Optional<T> をゼロまたは1の長さの Stream<T> . こんなことができるんですね。

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

三項演算子を flatMap はちょっと面倒なので、ちょっとしたヘルパー関数を書いておくといいかもしれませんね。

/**
 * 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() という操作になりますが、これは好みの問題です。