Java Gold SE 17のStream API対策・解説と例題

型・操作の順序・遅延評価を確認し、コードの出力と例題2問で理解を確かめましょう。

Streamのコードを読む4つの観点

  1. 生成時の型を確認する。Stream<Integer>とIntStreamは別の型です。途中のmap・mapToInt・boxedによって、次に使えるメソッドが変わります。
  2. 中間操作と終端操作を分ける。filterやmapは処理を組み立てる中間操作です。終端操作がないだけでコンパイルエラーになるわけではありません。
  3. 要素が通過する順序を追う。sorted().limit(2)とlimit(2).sorted()では、並べ替える対象が変わります。各段階で残る要素を書き出してください。
  4. 終端操作の後は再利用しない。もう一度処理する場合は、元のコレクションなどから新しいStreamを作ります。

sortedとlimit:操作の順序で結果が変わる

sortedは自然順序で並べ替え、limit(2)は先頭の2要素に絞ります。同じListから別々のStreamを作って比較します。

List<Integer> values = List.of(3, 1, 2);
System.out.println(values.stream()
    .sorted().limit(2).toList()); // [1, 2]
System.out.println(values.stream()
    .limit(2).sorted().toList()); // [1, 3]

前者は全体を並べ替えた[1, 2, 3]から2要素を取り、後者は先頭の[3, 1]だけを並べ替えます。途中で残る要素を追うと、結果の違いが分かります。

filterとtakeWhile:条件に合う要素と先頭部分の違い

Listから作った順序付きStreamで比較します。filterは条件に合う要素を残し、takeWhileは先頭から条件を満たす連続した部分だけを残します。

List<Integer> values = List.of(1, 2, 4, 3);
System.out.println(values.stream()
    .filter(n -> n < 4).toList()); // [1, 2, 3]
System.out.println(values.stream()
    .takeWhile(n -> n < 4).toList()); // [1, 2]

takeWhileは4で条件を満たさなくなるため、その後の3も結果に入りません。順序のないStreamでは、この先頭部分の規則をそのまま当てはめられません。詳細はtakeWhileの公式仕様で確認できます。

型変換とOptionalで間違えやすい違い

組み合わせ確認する点
map / flatMap各要素を変換するのか、各要素から作ったStreamを平坦化するのかを区別します。
findFirst / getfindFirstが返すOptionalが空かどうかを確認します。空のOptionalにgetを呼ぶと例外になります。

peekなどの副作用が必ず実行されると決めつけないでください。結果を変えない処理は最適化で省略される場合があります。仕様はJava SE 17のStream APIで確認できます。

例題2問:遅延評価と再利用

正解だけでなく、不正解の選択肢がどの規則と矛盾するかも考えてから解説を開いてください。

例題1:中間操作と遅延評価

Streamパイプラインの実行モデルについて、正しい説明を2つ選んでください。

2つ選択してください

  • A. 終端操作を書かないコードでもコンパイルは可能
  • B. parallelへ変更すればどの副作用の順序も順次処理と一致する
  • C. countの呼び出しは元のListから要素を削除する
  • D. filterを追加しただけでは通常その述語は実行されない
例題1の正解・解説を見る

正解:A・D

中間操作は処理の構成を記述し、終端操作が必要に応じて評価を開始します。

中間操作は処理の構成を記述し、終端操作が必要に応じて評価を開始します。

  • A:終端操作がないこと自体は構文エラーではありません。
  • B:順序依存の副作用の一致は保証されません。
  • C:通常の集約は元のコレクションを削除しません。
  • D:中間操作は遅延評価されます。

確認するポイント:副作用を使って処理順を推測する場合、短絡や最適化も考慮します。

仕様を確認するための公式資料:

問題ID:jg17-ff453db5d07383 · この問題の訂正を連絡する

例題2:終端操作後の再利用

次の処理が最後に表示する内容を選んでください。必要なimportは記述済みです。

var s = Stream.of("oak", "elm");
System.out.print(s.count() + ":");
try { s.findFirst(); }
catch (IllegalStateException e) { System.out.print("used"); }

1つ選択してください

  • A. 0:used
  • B. 2:Optional[oak]
  • C. 2:oak
  • D. 2:used
例題2の正解・解説を見る

正解:D

正解は「2:used」です。

countで消費したStreamはfindFirstで再利用できず、IllegalStateExceptionを捕捉します。

確認するポイント:終端操作後に同じ変数へ別の終端操作をつなげられません。

仕様を確認するための公式資料:

問題ID:jg17-a7759dc18e37a3 · この問題の訂正を連絡する

Stream APIの82問で復習する

Stream APIの演習では、10問から取り組めます。出題数は変更できます。遅延評価・型変換・集約・Optionalなどから苦手な問題を確認してください。82問は本サイトの収録数で、試験の出題比率ではありません。

解き直す際は、正解の番号を覚えるより「型が合わない」「終端操作済み」「空になる可能性がある」のように根拠を言葉にします。演算順序の例はsortedとdropWhileの例題でも確認できます。

次に取り組む学習

問題は本試験の過去問ではなく、Java SE 17を学ぶためのオリジナル練習教材です。編集・訂正の方針をご確認ください。