はじめに
JavaのストリームAPIについて、試験対策として整理しました。
ストリームAPIとは?
JavaのStream APIは、大量のデータに対して「何をしたいか」を宣言的に記述できる仕組みです。従来のforループに比べ、簡潔で可読性の高いコードが書けます。
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.forEach(System.out::println); // 出力: ALICE, CHARLIE
このように、中間操作 → 終端操作の流れで処理パイプラインを構成します。
Stream APIの特長
特長 |
説明 |
宣言的 |
「どうやるか」より「何をするか」を記述し、可読性が高い |
連結可能 |
.filter().map().sorted() のように処理をチェーンできる |
遅延評価 |
中間操作は終端操作が呼ばれるまで実行されない |
並列処理しやすい |
.parallelStream() で簡単に並列化可能 |
イミュータブル |
元のデータを変更せず、副作用の少ない処理が可能 |
ストリーム処理の3ステップ
ステップ |
説明 |
例 |
1. ストリーム生成 |
コレクションなどからstreamを生成する |
list.stream() |
2. 中間操作 |
絞り込み・変換などの処理(Streamを返す) |
filter() , map()
|
3. 終端操作 |
最終的な結果を取得または処理(値を返す) |
forEach() , count()
|
Streamとforループの違い
項目 |
forループ |
Stream API |
スタイル |
命令型(手続き的) |
宣言型(関数型) |
副作用 |
変数を多用しやすく副作用が生まれやすい |
副作用を極力排除 |
並列処理 |
自分で実装が必要 |
.parallelStream() で簡単に実装可能 |
集計結果取得 |
明示的にリストやカウンタが必要 |
collect() , count() などで簡単 |
注意点
- Streamは一度しか使えない(再利用不可)
- インデックス付きの処理は難しい
-
null
要素の扱いには注意(例外の原因に)
中間操作(intermediate operations)一覧
中間操作はストリームの変換・フィルタリングなどを行い、別のストリームを返します。
(主要なもののみ抜粋)
メソッド |
説明 |
引数 |
戻り値 |
filter |
条件に合致する要素のみ残す |
Predicate<T> |
Stream<T> |
map |
要素を変換(型変更も可) |
Function<T, R> |
Stream<R> |
flatMap |
ネストされた要素を平坦化 |
Function<T, Stream<R>> |
Stream<R> |
distinct |
重複要素の除去 |
なし |
Stream<T> |
sorted |
昇順ソート |
Comparator<T> (省略可) |
Stream<T> |
limit / skip
|
指定数の要素を取得/スキップ |
long |
Stream<T> |
takeWhile |
条件がtrueの間だけ要素を取得 |
Predicate<T> |
Stream<T> |
dropWhile |
条件がtrueの間はスキップ |
Predicate<T> |
Stream<T> |
peek |
各要素に副作用処理を追加(デバッグ) |
Consumer<T> |
Stream<T> |
終端操作(terminal operations)一覧
終端操作はストリームを処理し、最終的な結果を得る操作です。
メソッド |
説明 |
引数 |
戻り値 |
forEach |
各要素に処理を適用 |
Consumer<T> |
void |
collect |
Listなどへ収集 |
Collector |
Collection |
reduce |
要素を1つに集約 |
BinaryOperator など |
Optional/T |
count |
要素数をカウント |
なし |
long |
min / max
|
最小 / 最大値を取得 |
Comparator<T> |
Optional<T> |
findFirst |
最初の要素を返す(順序あり) |
なし |
Optional<T> |
findAny |
任意の要素を返す(並列向き) |
なし |
Optional<T> |
anyMatch |
条件に合う要素が1つでもあればtrue |
Predicate<T> |
boolean |
allMatch |
全要素が条件を満たすならtrue |
Predicate<T> |
boolean |
noneMatch |
条件を満たす要素がなければtrue |
Predicate<T> |
boolean |
使用例
List<String> names = List.of("Alice", "Bob", "Charlie", "David", "Bob");
List<String> result = names.stream()
.filter(name -> name.length() > 3)
.distinct()
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println(result); // [ALICE, CHARLIE, DAVID]
補足ポイント
-
reduce
は引数の数により戻り値が異なる:1つの場合は Optional<T>
、2つの場合は T
-
takeWhile
, dropWhile
はJava 9以降で追加された便利な短絡操作
-
forEachOrdered
は順序を保証(並列時に有効)
-
peek
は本番コードでは使用注意(副作用)
-
mapToInt
, mapToLong
, mapToDouble
などはプリミティブ向けのストリームに変換するための操作
BaseStream
とは?
java.util.stream.BaseStream<T, S extends BaseStream<T, S>>
は、すべてのStreamインターフェースの基本的な動作(例:iterator()
、spliterator()
、close()
など)を定義する 共通のインターフェース です。
BaseStream
の主なサブインターフェース一覧
インターフェース名 |
要素型 |
説明 |
Stream<T> |
任意のオブジェクト型 |
一般的なオブジェクトに使うストリーム |
IntStream |
int |
プリミティブ型 int のストリーム(ボクシング回避) |
LongStream |
long |
プリミティブ型 long のストリーム |
DoubleStream |
double |
プリミティブ型 double のストリーム |
各ストリームの特徴と使用例
Stream<T>
(オブジェクトストリーム)
- 任意のオブジェクト型(例:
String
, User
など)
- 最も一般的
Stream<String> names = Stream.of("Alice", "Bob");
IntStream
-
int
専用のストリーム。ボクシングのコスト回避
- 特有メソッド:
sum()
, average()
, range()
など
IntStream.range(1, 5).forEach(System.out::println); // 1~4
LongStream
LongStream.of(100L, 200L, 300L);
DoubleStream
DoubleStream.of(1.5, 2.5, 3.5).average();
プリミティブストリームの活用ポイント
目的 |
使用するストリーム |
一般的なオブジェクト処理 |
Stream<T> |
数値・統計処理 |
IntStream , LongStream , DoubleStream
|
ボクシング回避による性能最適化 |
プリミティブストリーム |
合計・平均・統計情報取得 |
プリミティブストリーム専用メソッド |
型変換:プリミティブ → オブジェクト
IntStream.range(1, 4)
.boxed()
.collect(Collectors.toList()); // Stream<Integer>
IntStreamの中間操作一覧
メソッド |
内容 |
引数 |
戻り値 |
filter |
条件に一致する要素を残す |
IntPredicate |
IntStream |
map |
int同士の変換 |
IntUnaryOperator |
IntStream |
mapToObj |
オブジェクトに変換 |
IntFunction<R> |
Stream<R> |
flatMap |
複数のIntStreamに展開 |
IntFunction<IntStream> |
IntStream |
distinct , sorted , limit , skip , takeWhile , dropWhile , peek
|
共通操作 |
型ごとの関数型インターフェース |
IntStream |
boxed , asLongStream , asDoubleStream
|
型変換用 |
- |
他のStream |
IntStream専用中間操作(Streamでは使えない)
メソッド |
説明 |
mapToObj , mapToLong , mapToDouble
|
型変換用 |
flatMap |
IntFunction<IntStream> を使用 |
boxed |
Integer へボクシング |
asLongStream , asDoubleStream
|
型変換用 |
Streamで使えてIntStreamで使えない中間操作
メソッド |
説明 |
map(Function) , flatMap(Function)
|
汎用的な型変換と展開 |
sorted(Comparator) |
カスタムソート |
unordered , onClose
|
並列・終了時処理 |
peek(Consumer) |
汎用Consumer型の副作用処理 |
IntStreamの終端操作一覧
メソッド |
説明 |
戻り値 |
forEach , forEachOrdered
|
各要素に処理 |
void |
toArray() |
int[] に変換 |
int[] |
reduce , sum , min , max , average
|
集計 |
OptionalInt , int , OptionalDouble など |
summaryStatistics |
統計取得 |
IntSummaryStatistics |
collect(Supplier, ObjIntConsumer, BiConsumer) |
集計カスタマイズ |
任意型 R
|
anyMatch , allMatch , noneMatch , findFirst , findAny
|
条件・検索 |
boolean , OptionalInt
|
IntStream専用終端操作(Streamでは使えない)
sum()
average()
-
min()
, max()
(→ OptionalInt
)
summaryStatistics()
Streamで使えてIntStreamで使えない終端操作
メソッド |
説明 |
collect(Collector) |
Collectors.toList() など簡易集約 |
reduce(BinaryOperator<T>) |
ジェネリクス対応の汎用集約 |
toArray(IntFunction<A[]>) |
型付き配列変換 |
iterator() , spliterator()
|
イテレータ・スプリッタ取得 |
close() , onClose()
|
リソースクローズ処理 |
DoubleStream / LongStream の特有メソッド
メソッド |
戻り値 |
説明 |
sum() , average() , min() , max()
|
double , OptionalDouble , OptionalLong
|
数値集計 |
summaryStatistics() |
統計オブジェクト |
全体の統計情報まとめて取得 |
mapToX() , boxed() , asXStream()
|
他型への変換 |
|
DoubleStream/LongStreamで使えないStream専用メソッド
メソッド |
説明 |
collect(Collector) |
Collectors.toList() など非対応 |
map(Function) , flatMap(Function)
|
オブジェクト用 |
sorted(Comparator) |
カスタムソート非対応 |
onClose() , unordered()
|
ストリーム制御関連 |
toArray(IntFunction<A[]>) |
型付き配列非対応 |
おわりに
誤記や改善点があればコメントなどでご指摘ください。