ホーム › 関数型プログラミング › Func-02
Func-02: Stream API と関数型インターフェースの組み合わせ
Stream API の各メソッドは、内部で関数型インターフェースを受け取る設計になっています。filter() には Predicate、map() には Function、forEach() には Consumer、generate() には Supplier を渡します。 この対応関係を理解することで、Stream API をより自在に使いこなせるようになります。
Stream メソッドと関数型インターフェースの対応
Stream API のメソッドに何を渡せばよいか迷ったときは、このテーブルを参考にしてください。 受け取る関数型インターフェースを理解すると、「どんなラムダ式を書けばよいか」が自然にわかるようになります。
| Stream メソッド | 受け取る型 | ラムダ式の形 | 用途 |
|---|---|---|---|
filter() | Predicate<T> | T → boolean | 条件に合う要素だけ残す |
map() | Function<T, R> | T → R | 各要素を別の型に変換する |
forEach() | Consumer<T> | T → void | 各要素に対して処理を実行する |
generate() | Supplier<T> | () → T | 無限ストリームを生成する |
collect(groupingBy()) | Function<T, K> | T → K(グループキー) | Map にグルーピングする |
サンプルコード
Java 8 では Stream の各中間操作に対応する関数型インターフェースが決まっています。filter() は Predicate、map() は Function、forEach() は Consumer、generate() は Supplier を受け取ります。これらを変数に切り出すことで、ロジックを再利用しやすくなります。
よくあるミス・注意点
⚠️ Stream は遅延評価 — 終端操作を呼ばないと何も実行されない
Stream の filter() やmap() などの「中間操作」は、 それだけでは実行されません。collect()、forEach()、count() などの「終端操作」を呼んだときに初めて処理が走ります。 「filter() を書いたのに処理されない」と感じたときは、終端操作の呼び忘れがないか確認しましょう。
⚠️ Stream は一度しか使えない — 再利用しようとすると例外が発生する
Stream オブジェクトは終端操作を呼んだ後に「消費済み」になります。 同じ Stream オブジェクトに対して再度終端操作を呼ぶとIllegalStateException が発生します。 Stream を複数回使いたい場合は、元のコレクションから都度stream() を呼び直してください。
テストする観点
filter().collect()で条件に合う要素だけが残ること(境界値: 価格がちょうど 5000 円のとき含まれるか)Predicate.and()で2条件をともに満たす要素だけに絞れることgroupingBy()の結果として、各カテゴリに正しい要素が振り分けられることmapToInt().sum()でカテゴリ別の合計金額が正しく計算されること- 空のリストに対して Stream 操作を行っても例外が発生せず、空の結果が返ること