導入
Composite パターンのレッスンで、for ループを使って List<FileSystemItem> の合計サイズを計算しました。「リストの各要素を処理して、まとめる」という操作は非常によく出てきます。Stream API は、こうした「集める・変換する・絞り込む」処理を、ループを自分で書かずに宣言的(「何をしたいか」だけを書く)に表現する仕組みです。
説明
import java.util.List;
List<Integer> prices = List.of(300, 500, 1200, 80, 950);
// 従来のforループ
int total = 0;
for (int p : prices) {
if (p >= 500) {
total += p;
}
}
// Streamで書くと「何をしたいか」がそのまま読める
int totalStream = prices.stream()
.filter(p -> p >= 500) // 500円以上だけ残す
.mapToInt(p -> p) // int の並びとして扱う
.sum(); // 合計する
flowchart LR A["List<Integer> prices"] --> B["stream()"] B --> C["filter(p -> p >= 500)"] C --> D["mapToInt(...)"] D --> E["sum()"]
prices.stream()… リストを「処理の流れ(ストリーム)」に変換します。.filter(p -> p >= 500)… 条件に合うものだけを残します(絞り込み)。ここでもラムダ式が活躍しています。.mapToInt(...)… 各要素を変換します(今回はそのままintとして扱うだけ)。.sum()… 最終的に1つの値にまとめます。- 一連の処理を
.でつなげて書くスタイルは、第2章のBuilderパターンのメソッドチェーンと見た目がよく似ています。Streamも「途中の状態(ストリーム)を返しながら、次の操作につなげていく」という点で、考え方に共通点があります。 - Composite パターンの
Folder.getSize()のforループも、Streamを使えばchildren.stream().mapToInt(FileSystemItem::getSize).sum()のように書き換えられます。パターンの構造(木構造で全体と部分を同じに扱う)自体は変わりませんが、集計の実装がより簡潔になります。