2.3. stream apiとopeionalの組み合わせ
JavaのOptionalとStreamは非常に相性が良く、組み合わせて使うことで「データがないかもしれない」状況をスマートに処理できます。
実戦でよく使われるパターンを網羅した練習問題を作成しました。
練習問題:ユーザー情報のフィルタリング¶
以下の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)」のみを抽出し、リストとして取得**してください。
条件:
User::getAgeはOptional<Integer>を返します。- 年齢が
null(Empty)の場合は除外してください。 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());
ステップアップ:よく使う「組み合わせの型」¶
Optional と Stream を組み合わせる際、以下の3つのパターンが頻出します。
| パターン | メソッド | 用途 |
|---|---|---|
| 展開 | .flatMap(Optional::stream) |
List<Optional<T>> から値があるものだけを取り出す |
| 検索 | .findFirst() / .findAny() |
Stream の結果を Optional で受け取る |
| 変換 | .map(obj::getOptional) |
ストリームの要素から Optional な値を取得する |
次のステップへの提案¶
この練習問題が解けたら、次は**「ネストしたOptionalをflatMapで平坦化する」パターンや、「Streamの結果が空だった場合にOptional.or()でデフォルト値を返す」**ような、より複雑なロジックに挑戦してみませんか?
ご希望であれば、さらに難易度を上げた問題を作成します。