本文へスキップ
BecomeCoder

Javaデザインパターンコース · 第5章 モダンJavaとパターン ― 言語機能で置き換えられるもの · レッスン16

Stream ― コレクション操作の「手順」を宣言的に書く

ローカル実施

導入

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() のように書き換えられます。パターンの構造(木構造で全体と部分を同じに扱う)自体は変わりませんが、集計の実装がより簡潔になります。