Skip to content
閲覧中:
2.3. stream apiとopeionalの組み合わせ

2.3. stream apiとopeionalの組み合わせ

JavaのOptionalStreamは非常に相性が良く、組み合わせて使うことで「データがないかもしれない」状況をスマートに処理できます。

実戦でよく使われるパターンを網羅した練習問題を作成しました。


練習問題:ユーザー情報のフィルタリング

以下のUserクラスと、データのリストがあるとします。

Java
import java.util.*;
import java.util.stream.*;

class User {
    private String name;
    private Integer age; // nullの可能性がある

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public Optional<Integer> getAge() { return Optional.ofNullable(age); }
}

【問題】

以下のList<User>を元に、**「年齢が20歳以上のユーザーの名前(String)」のみを抽出し、リストとして取得**してください。

条件:

  1. User::getAgeOptional<Integer> を返します。
  2. 年齢が null(Empty)の場合は除外してください。
  3. Stream 内で Optional を適切に処理してください。
Java
List<User> users = Arrays.asList(
    new User("Alice", 25),
    new User("Bob", null),
    new User("Charlie", 18),
    new User("David", 30)
);

// ここに処理を記述
List<String> adultNames = ...

解答例と解説

この問題のポイントは、Stream の途中で出てくる Optional をどうやって「中身があるものだけ」に絞り込むかです。

解答1:Java 9以降(Optional::stream を使用)

現在、最も推奨されるスマートな書き方です。

Java
List<String> adultNames = users.stream()
    .filter(u -> u.getAge().map(age -> age >= 20).orElse(false)) // 20歳以上でフィルタ
    .map(User::getName)
    .collect(Collectors.toList());

または、Optional を展開してからフィルタリングする場合:

Java
List<String> adultNames = users.stream()
    .flatMap(u -> u.getAge().stream()) // OptionalをStreamに変換 (値がなければ空Stream)
    .filter(age -> age >= 20)          // ここでは Integer として扱える
    // ※ ただし、これだと名前(Userオブジェクト)が消えてしまうので、目的に応じて使い分けます

解答2:名前を取得する流れで Optional を処理

「名前リスト」を作るという目的に忠実な書き方です。

Java
List<String> result = users.stream()
    .filter(u -> u.getAge()
                  .filter(age -> age >= 20) // Optionalの中身で条件判定
                  .isPresent())             // 条件に合うAgeを持っているか
    .map(User::getName)
    .collect(Collectors.toList());

ステップアップ:よく使う「組み合わせの型」

OptionalStream を組み合わせる際、以下の3つのパターンが頻出します。

パターン メソッド 用途
展開 .flatMap(Optional::stream) List<Optional<T>> から値があるものだけを取り出す
検索 .findFirst() / .findAny() Stream の結果を Optional で受け取る
変換 .map(obj::getOptional) ストリームの要素から Optional な値を取得する

次のステップへの提案

この練習問題が解けたら、次は**「ネストしたOptionalをflatMapで平坦化する」パターンや、「Streamの結果が空だった場合にOptional.or()でデフォルト値を返す」**ような、より複雑なロジックに挑戦してみませんか?

ご希望であれば、さらに難易度を上げた問題を作成します。