Java 逆引き集 | Stream.collect(Collectors.toList()/toSet()/toMap()) — ストリーム結果変換

Java Java
スポンサーリンク

Stream.collect(Collectors.toList()/toSet()/toMap()) — ストリーム結果変換

Stream の終端操作である collect は、加工した要素を List・Set・Map にまとめるための定番メソッドです。filter・map・sorted などの中間操作のあと、最終形に「変換」するのが collect の役割です。


基本の考え方

  • 役割: 加工済みのストリーム要素をコレクションへ「集約」する終端操作。ストリームは一度流したら再利用しないので、結果は collect で取り出す。
  • 主な変換先: Collectors.toList()Collectors.toSet()Collectors.toMap(keyMapper, valueMapper, ...) が軸。目的に合わせて使い分ける。

toList と toSet(順序のまま/重複排除)

import java.util.*;
import java.util.stream.*;

List<String> names = List.of("Tanaka", "Sato", "Sato");

// List へ(元順序を保ちやすい)
List<String> list = names.stream()
    .filter(s -> s.length() >= 4)
    .collect(Collectors.toList());

// Set へ(重複排除)
Set<String> set = names.stream()
    .collect(Collectors.toSet());
Java
  • ポイント:
    • toList: フィルタや変換後の結果を順序付きで受け取りたいときの基本形。
    • toSet: 要素の重複をなくしたいときに使う(順序は実装依存)。

toMap(キー/値を指定して Map 化)

import java.util.*;
import java.util.stream.*;

record User(String name, int age) {}

List<User> users = List.of(
    new User("Tanaka", 30), new User("Sato", 25), new User("Kato", 25)
);

// 基本:キーと値を指定
Map<String, Integer> byNameAge = users.stream()
    .collect(Collectors.toMap(User::name, User::age));

// 重複キーが起きる場合はマージ関数を指定
Map<Integer, String> namesByAge = users.stream()
    .collect(Collectors.toMap(
        User::age,
        User::name,
        (a, b) -> a + "," + b   // 同じキー(age)にぶつかったら結合
    ));
Java
  • ポイント:
    • キー重複対策: toMap はキーが重複すると例外。第三引数のマージ関数で解決方法を必ず指定する。
    • LinkedHashMap にしたい: 4番目の引数で LinkedHashMap::new を渡すと挿入順が保たれる。ランキングや順序付きの結果に便利。
Map<String, Integer> ordered = users.stream()
    .sorted(Comparator.comparingInt(User::age).reversed())
    .collect(Collectors.toMap(
        User::name,
        User::age,
        (a,b) -> a,
        LinkedHashMap::new
    ));
Java

例題で身につける

例題1: 単語のフィルタ+整形を List に集約

List<String> words = List.of("apple", "banana", "apricot", "pear");
List<String> startWithA = words.stream()
    .filter(w -> w.startsWith("a"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());
Java
  • ねらい: 中間操作→終端操作の流れを体感(filter→map→collect)。

例題2: 重複排除して Set に集約

List<Integer> nums = List.of(1, 2, 2, 3, 3, 3);
Set<Integer> unique = nums.stream()
    .collect(Collectors.toSet()); // [1,2,3](順序は実装依存)
Java
  • ねらい: 集約で重複をなくす(集合的な扱い)。

例題3: 配列の toMap(ID→オブジェクト)

record Item(String id, String name) {}

List<Item> items = List.of(
    new Item("A001", "Apple"),
    new Item("B002", "Banana"),
    new Item("C003", "Cherry")
);
Map<String, Item> byId = items.stream()
    .collect(Collectors.toMap(Item::id, i -> i));
Java
  • ねらい: 参照戻しに便利な「キー→値」辞書の作成。

よくある落とし穴と回避策

  • 落とし穴: toMap でキー重複が発生して例外。
    • 回避: 第三引数で「重複時の結合ルール」を指定する(加算、先勝、後勝、連結など)。
  • 落とし穴: 結果の順序を期待しているのに Map が並ばない。
    • 回避: 4番目の引数に LinkedHashMap::new を渡して挿入順を保持する。ソート後に collect するとその順序が保てる。
  • 落とし穴: null をキーや値にしたい。
    • 回避: toMap の keyMapper/valueMapper は null 非推奨。null を生成しないマッピングを設計する。
  • 落とし穴: 巨大データで toSet の順序が必要。
    • 回避: 順序維持が必要なら toCollection(LinkedHashSet::new) を使う。

ミニテンプレート集

  • 基本の List 化
List<T> result = stream.collect(Collectors.toList());
Java
  • 重複排除の Set 化
Set<T> result = stream.collect(Collectors.toSet());
Java
  • 重複キー対策付き toMap
Map<K,V> m = stream.collect(Collectors.toMap(
    x -> keyOf(x),
    x -> valueOf(x),
    (a,b) -> merge(a,b)
));
Java
  • 順序保持の LinkedHashMap で収集
Map<K,V> m = stream.collect(Collectors.toMap(
    kf, vf, (a,b) -> a, LinkedHashMap::new
));
Java
  • コレクション種別を指定して収集
LinkedHashSet<T> s = stream.collect(Collectors.toCollection(LinkedHashSet::new));
Java

補足(collect の位置づけ)

  • 中間 vs 終端: filter・map などの「中間操作」で形を整え、collect の「終端操作」で結果を取り出すという流れが基本。宣言的に処理を組み立てられるのが Stream の利点。
  • 使い捨てモデル: ストリームは一度流したら再利用しないため、繰り返し使うデータは collect したコレクションを保持する。

まとめ

  • collect はストリーム結果をコレクションに変換する終端操作。とくに toListtoSettoMap は基本中の基本。
  • toMap では重複キー対策と順序保持(LinkedHashMap 指定)が実務で重要。filter・map などと組み合わせて、読みやすく安全なデータ処理パイプラインを作れる。
タイトルとURLをコピーしました