2.1 stream api
Java SE 11 Goldにおいて、Stream APIは最も問題数が多い「得点源」です。しかし、基本(filter, map, forEach)だけでは不十分で、「型変換(特化ストリーム)」**と「複雑な集計(Collectors)」**が合否を分けます。
重要ポイントを3つのセクションに分けて解説します。
1. プリミティブ特化ストリームと型変換¶
Stream<Integer> のような参照型のストリームは、ボクシング(型変換)のコストがかかるため、Javaには数値専用のストリームが用意されています。
- 3つの特化ストリーム:
IntStream,LongStream,DoubleStream - 専用メソッド:
sum(),average(),min(),max()などの統計用メソッド(Stream<T>にはない)。range(1, 10)(1〜9) やrangeClosed(1, 10)(1〜10) の連番生成。
変換の向きに注意!¶
試験では「今、どの型(ストリーム)を扱っているか」を問う問題が頻出します。
- オブジェクト → 数値:
mapToInt(),mapToLong(),mapToDouble() - 数値 → オブジェクト:
mapToObj() - 数値 → 数値(別の型):
asLongStream(),asDoubleStream()
2. 複雑な集計:Collectors クラス¶
collect() メソッドの中で使われる Collectors の引数は、非常に読み取りづらく「ひっかけ」の宝庫です。
groupingBy vs partitioningBy¶
groupingBy(Function): 任意のキーでグループ化します(戻り値はMap<K, List<T>>)。partitioningBy(Predicate): 条件に合うか合わないか(true/false)の2つに分けます(戻り値はMap<Boolean, List<T>>)。
第2引数によるネスト(Downstream)¶
groupingBy の第2引数にさらに別の Collector を渡すパターンです。
// 部署ごとに「人数」を集計する
Map<Department, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDept, Collectors.counting()));
試験のコツ:
groupingByの戻り値がMap<K, List<T>>なのか、それとも第2引数によってMap<K, Long>やMap<K, Optional<T>>になっているのかを注意深く見てください。
3. Optional の「型」¶
特化ストリームの終端操作(max, min, averageなど)は、要素が空の場合を考慮して Optionalの数値版 を返します。
IntStream.average()→OptionalDoubleを返す(※平均は小数になるため)IntStream.max()→OptionalIntを返す
これらには get() ではなく、getAsInt() や getAsDouble() というメソッド名が使われます。ここが get() と書かれていてコンパイルエラー、という問題もよく出ます。
4. 終端操作と短絡操作(Short-circuiting)¶
効率化のための挙動も重要です。
-
短絡操作:
anyMatch,allMatch,noneMatch,findFirst,findAny- すべての要素を調べなくても、結果が決まった時点で処理を終了します。
-
非干渉(Non-interference):
- ストリーム処理中に、元のコレクションの要素を変更(add/remove)してはいけません。実行時に
ConcurrentModificationExceptionが発生します。
- ストリーム処理中に、元のコレクションの要素を変更(add/remove)してはいけません。実行時に
次に確認すべきこと¶
Stream APIと密接に関係するのが 「関数型インターフェース(PredicateやFunctionなど)」 です。
もし「どのメソッドがどの引数を取るか(例:map は Function だが filter は Predicate など)」に不安があれば、そこを整理しましょうか?
あるいは、SE 11 Goldのもう一つの難所である 「NIO.2 (Files/Path)」 について解説しましょうか?
なお、各種アプリのすべての機能を使用するには、Gemini アプリ アクティビティを有効にする必要があります。