Kodrill

Java ストリーム API

Java SE 17 Gold (1Z0-826-JPN) 試験

Stream の遅延評価/中間操作/reduce・collect と Collectors/集計・探索の終端操作/Optional/並列ストリーム

問題数
29 問
合格ライン
65%
実行して確認済み
26 問
この分野の要点を読む7 章・約 23 分。実行して確かめたコード例と、試験でよくある引っかけをまとめています

ログインなしで試す

無料の 5 問を、1問ごとに正誤と解説を見ながら解けます。解答は保存されません。

5 問を試す

無料登録して学習を記録する

学習履歴・ダッシュボード・ブックマーク・模試モードが使えます。

★ 残り 24 問はプレミアム会員限定です

無料プランでは先頭 5 問まで解けます。プレミアムなら全問題・苦手問題の復習・広告非表示が使えます。
料金プランを見る

問題と解説を見る (無料)

登録しなくても、次の5問は問題文と解説をそのまま読めます。

Java ストリーム APIの要点

内容の確認日: 2026-10-04

この分野は Gold で最も出題数が多く、ストリームの処理順と、中間操作・終端操作・Collectors の各メソッドが何を返すかが細かく問われます。特に「いつ、どの順番で処理が実行されるか」(遅延評価と短絡評価) を理解していないと、出力を答える問題で間違えます。

出題項目は「Stream API の概要」「Stream の中間操作 (filter, map, peek, sorted および flatMap メソッドなど)」「Stream のリダクション処理 (reduce, collect メソッドおよび Collectors の使用)」「Stream の集計処理」「Stream の探索処理」「java.util.Optional クラスの使用」「ParallelStream を使用した並列処理」の 7 つです。

遅延評価と処理の順番

ストリームの中間操作 (filter・map・peek など) は、呼び出しただけでは実行されず、終端操作 (forEach・collect・count など) が呼ばれたときに初めて実行されます (遅延評価)。終端操作がなければ、何も実行されません。

また、ストリームは「すべての要素に filter → すべての要素に map」のように操作ごとにまとめて処理するのではなく、要素を 1 つずつ、パイプラインの最後まで流してから次の要素に進みます。findFirst のような短絡操作は、結果が決まった時点で残りの要素を処理しません。

Main.java
import java.util.List;​public class Main {    public static void main(String[] args) {        List.of("a", "bb", "ccc").stream()            .filter(s -> {                System.out.println("filter " + s);                return s.length() > 1;            })            .map(s -> {                System.out.println("map " + s);                return s.toUpperCase();            })            .findFirst()            .ifPresent(s -> System.out.println("結果 " + s));    }}
ターミナル
java Main
filter a
filter bb
map bb
結果 BB

ccc は、bb で結果が決まったので処理されません。また、ストリームは一度しか使えません。終端操作を実行した後のストリームをもう一度使うと、IllegalStateException が発生します。

Main.java
import java.util.stream.Stream;​public class Main {    public static void main(String[] args) {        Stream<String> s = Stream.of("x", "y");        System.out.println(s.count());        s.forEach(System.out::println);    }}
ターミナル
java Main
2

実行時に例外が発生: IllegalStateException

中間操作

主な中間操作は次のとおりです。

・filter(条件): 条件に合う要素だけを残す ・map(関数): 要素を変換する。mapToInt などでプリミティブ型のストリームに、boxed() や mapToObj で Stream に戻せる ・flatMap(関数): 各要素をストリームに変換し、1 つのストリームにつなげる (平坦化) ・distinct(): 重複を除く ・sorted()・sorted(Comparator): 並べ替える ・skip(n)・limit(n): 先頭の n 個を飛ばす・先頭の n 個だけにする ・peek(処理): 要素を変えずに処理を挟む (デバッグ用)

IntStream.range(1, 4) は終わりの値を含まない 1, 2, 3、IntStream.rangeClosed(1, 4) は含む 1, 2, 3, 4 を作ります。

Main.java
import java.util.Comparator;import java.util.List;import java.util.stream.Collectors;import java.util.stream.IntStream;​public class Main {    public static void main(String[] args) {        List<List<Integer>> nested = List.of(List.of(2, 7), List.of(7, 9), List.of(4));        List<Integer> result = nested.stream()            .flatMap(List::stream)            .distinct()            .sorted(Comparator.reverseOrder())            .skip(1)            .limit(2)            .collect(Collectors.toList());        System.out.println(result);        System.out.println(IntStream.rangeClosed(1, 4).sum() + " " + IntStream.range(1, 4).sum());    }}
ターミナル
java Main
[7, 4]
10 6

2, 7, 7, 9, 4 → distinct で 2, 7, 9, 4 → 降順で 9, 7, 4, 2 → skip(1) で 7, 4, 2 → limit(2) で 7, 4 です。

リダクション: reduce と collect

reduce は要素を 1 つの値にまとめます。単位元 (初期値) を渡す reduce(単位元, 演算) は値そのものを返し、単位元を渡さない reduce(演算) は、要素がない場合に備えて Optional を返します。

collect には Collectors のメソッドで作ったコレクタを渡します。よく使うものは次のとおりです。

・toList()・toSet()・joining(区切り, 前, 後) ・groupingBy(分類関数): 分類関数の結果をキーにした Map<K, List<T>>。第 2 引数にマップの生成方法 (TreeMap::new など)、最後の引数に下流のコレクタ (counting() など) を指定できる ・partitioningBy(条件): true と false の 2 グループに分けた Map<Boolean, List<T>>。該当する要素がなくても両方のキーがある ・mapping(変換, 下流のコレクタ): 要素を変換してから下流のコレクタに渡す ・toMap(キーの関数, 値の関数): Map を作る

Main.java
import java.util.List;import java.util.Map;import java.util.Optional;import java.util.TreeMap;import java.util.stream.Collectors;​record Book(String title, String genre, int price) {}​public class Main {    public static void main(String[] args) {        List<Book> books = List.of(            new Book("A", "IT", 3000), new Book("B", "NOVEL", 800),            new Book("C", "IT", 2500), new Book("D", "NOVEL", 1200));​        int total = books.stream().map(Book::price).reduce(0, Integer::sum);        Optional<Integer> max = books.stream().map(Book::price).reduce(Integer::max);        System.out.println(total + " " + max.get());​        Map<String, Long> countByGenre = books.stream()            .collect(Collectors.groupingBy(Book::genre, TreeMap::new, Collectors.counting()));        System.out.println(countByGenre);​        Map<Boolean, List<String>> expensive = books.stream()            .collect(Collectors.partitioningBy(b -> b.price() >= 2000,                Collectors.mapping(Book::title, Collectors.toList())));        System.out.println(expensive);​        System.out.println(books.stream().map(Book::title).collect(Collectors.joining(", ", "[", "]")));    }}
ターミナル
java Main
7500 3000
{IT=2, NOVEL=2}
{false=[B, D], true=[A, C]}
[A, B, C, D]

toMap は、キーが重複すると IllegalStateException を投げます。重複がありうる場合は、第 3 引数に値を合成する関数 ((a, b) -> a + "," + b など) を渡します。

Main.java
import java.util.Map;import java.util.stream.Collectors;import java.util.stream.Stream;​public class Main {    public static void main(String[] args) {        Map<String, String> byInitial = Stream.of("apple", "avocado")            .collect(Collectors.toMap(s -> s.substring(0, 1), s -> s));        System.out.println(byInitial);    }}
ターミナル
java Main

実行時に例外が発生: IllegalStateException

集計と探索の終端操作

count() は long を返します。IntStream などのプリミティブ型のストリームには sum()・average()・max()・min() があり、average() は OptionalDouble、max()・min() は OptionalInt を返します。空のストリームの sum() は 0 ですが、average() は値のない OptionalDouble です。summaryStatistics() を使うと、件数・合計・最小・最大・平均を 1 回でまとめて求められます。

Stream<T> の max・min には、比較方法を決める Comparator を渡す必要があります (引数なしでは呼べません)。

Main.java
import java.util.Comparator;import java.util.IntSummaryStatistics;import java.util.stream.IntStream;import java.util.stream.Stream;​public class Main {    public static void main(String[] args) {        IntSummaryStatistics st = IntStream.of(4, 8, 3).summaryStatistics();        System.out.println(st.getMin() + " " + st.getMax() + " " + st.getAverage());        System.out.println(IntStream.empty().sum() + " " + IntStream.empty().average().isPresent());        System.out.println(Stream.of("kiwi", "fig", "banana").max(Comparator.comparing(String::length)).get());    }}
ターミナル
java Main
3 8 5.0
0 false
banana

探索の終端操作 findFirst・findAny は Optional を返します。anyMatch・allMatch・noneMatch は boolean を返し、結果が決まった時点で残りの要素を処理しない短絡操作です。空のストリームでは、anyMatch は false、allMatch と noneMatch は true になります。

Main.java
import java.util.stream.Stream;​public class Main {    public static void main(String[] args) {        boolean all = Stream.of(2, 4, 5, 6)            .peek(n -> System.out.print(n + " "))            .allMatch(n -> n % 2 == 0);        System.out.println("-> " + all);        System.out.println(Stream.empty().allMatch(x -> false) + " " + Stream.empty().anyMatch(x -> true));    }}
ターミナル
java Main
2 4 5 -> false
true false

Optional

Optional は、値があるかないかを表す入れ物です。作り方と取り出し方の主なメソッドは次のとおりです。

・Optional.of(値): null を渡すと、その場で NullPointerException ・Optional.ofNullable(値): null なら空の Optional ・Optional.empty(): 空の Optional ・get(): 値を返す。空なら NoSuchElementException ・orElse(値)・orElseGet(関数)・orElseThrow(): 空のときの代わりの値・代わりの値を作る関数・例外 ・map・filter・ifPresent・isPresent・isEmpty

orElse の引数は普通のメソッドの引数なので、Optional に値があっても呼び出す前に評価されます。orElseGet に渡した関数は、空のときだけ実行されます。

Main.java
import java.util.Optional;​public class Main {    static String fallback() {        System.out.println("fallback 実行");        return "default";    }​    public static void main(String[] args) {        Optional<String> name = Optional.of("gold");        System.out.println(name.orElse(fallback()));        System.out.println(name.orElseGet(Main::fallback));​        Optional<String> none = Optional.ofNullable(null);        System.out.println(none.map(String::length).orElse(-1));        System.out.println(name.filter(s -> s.length() > 5).isPresent());    }}
ターミナル
java Main
fallback 実行
gold
gold
-1
false

並列ストリーム

parallelStream() や parallel() で得られる並列ストリームは、要素を複数のスレッドで分担して処理します。並列ストリームでも、順序を持つストリーム (List や range から作ったもの) を collect で集めると、結果の順序は保たれます。一方、forEach は処理したスレッドの都合で順番が変わります。元の順序で処理したいときは forEachOrdered を使います。

Main.java
import java.util.List;import java.util.stream.Collectors;import java.util.stream.IntStream;​public class Main {    public static void main(String[] args) {        List<Integer> list = IntStream.rangeClosed(1, 5).boxed().collect(Collectors.toList());        List<Integer> doubled = list.parallelStream().map(n -> n * 2).collect(Collectors.toList());        System.out.println(doubled);        list.parallelStream().forEachOrdered(n -> System.out.print(n + " "));        System.out.println();        System.out.println(list.parallelStream().reduce(0, Integer::sum));    }}
ターミナル
java Main
[2, 4, 6, 8, 10]
1 2 3 4 5
15

並列ストリームの reduce では、要素をいくつかのグループに分けて合成するので、第 1 引数は本当の単位元 (足し算なら 0、掛け算なら 1) でなければなりません。reduce(10, Integer::sum) のように単位元でない値を渡すと、10 がグループの数だけ足されて、結果が逐次処理と変わることがあります。

試験でよくある引っかけ

  • 終端操作のないストリーム

    中間操作だけでは何も実行されません。peek の出力を問う問題では、終端操作があるかを最初に確かめましょう。

  • ストリームの再利用

    終端操作を実行したストリームをもう一度使うと IllegalStateException です。

  • reduce の戻り値の型

    単位元ありの reduce(0, ...) は値、単位元なしの reduce(...) は Optional を返します。

  • toMap のキーの重複

    キーが重複すると IllegalStateException です。第 3 引数で値の合成方法を指定します。

  • 空のストリームの allMatch

    空のストリームでは allMatch と noneMatch は true、anyMatch は false です。

  • orElse と orElseGet

    orElse(fallback()) は値があっても fallback() を実行します。空のときだけ実行したいなら orElseGet。

問題で確認する

この分野の問題を、解説つきで読めます (登録不要)。

出典・参考

コード例は、試験の対象バージョンで実際にコンパイル・実行して結果を確認しています。