0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaのStream.collect()を使いこなす — toList以外も覚えよう

0
Posted at

JavaのStream.collect()を使いこなす — toList以外も覚えよう

はじめに

Stream APIの終端操作といえば collect(Collectors.toList()) が定番ですが、Collectors にはほかにも便利なメソッドがたくさんあります。

この記事では実務でよく使う collect() のパターンをまとめます。


collect()の基本

collect() はStreamの要素をコレクションや文字列などにまとめる終端操作です。

List<String> names = List.of("Alice", "Bob", "Charlie");

// Streamに変換して処理し、collect()でまとめる
List<String> result = names.stream()
    .filter(name -> name.length() > 3)
    .collect(Collectors.toList());

System.out.println(result); // [Alice, Charlie]

引数に渡す Collectors.toList() などをコレクターと呼びます。コレクターを変えることで出力の形式を柔軟に変えられます。


toList() — リストにまとめる

最もよく使う基本パターンです。

List<String> names = List.of("Alice", "Bob", "Charlie", "Dave");

List<String> longNames = names.stream()
    .filter(name -> name.length() > 3)
    .collect(Collectors.toList());

System.out.println(longNames); // [Alice, Charlie, Dave]

Java 16以降Stream.toList() と書けてさらに短くなります。

// Java 16以降のショートカット(変更不可リストを返す)
List<String> longNames = names.stream()
    .filter(name -> name.length() > 3)
    .toList();

toSet() — 重複を除いてSetにまとめる

List<Integer> numbers = List.of(1, 2, 2, 3, 3, 3);

Set<Integer> unique = numbers.stream()
    .collect(Collectors.toSet());

System.out.println(unique); // [1, 2, 3](順序は保証されない)

重複を除きたい場合に使います。順序が必要なら Collectors.toCollection(TreeSet::new) で対応できます。


joining() — 文字列として結合する

List<String> names = List.of("Alice", "Bob", "Charlie");

// 単純に連結
String joined = names.stream()
    .collect(Collectors.joining());
System.out.println(joined); // "AliceBobCharlie"

// 区切り文字を指定
String csv = names.stream()
    .collect(Collectors.joining(", "));
System.out.println(csv); // "Alice, Bob, Charlie"

// 区切り文字 + 前後の文字を指定
String formatted = names.stream()
    .collect(Collectors.joining(", ", "[", "]"));
System.out.println(formatted); // "[Alice, Bob, Charlie]"

CSV出力やログ用の文字列生成でよく使います。


toMap() — Mapに変換する

List<String> names = List.of("Alice", "Bob", "Charlie");

// 名前 → 文字数 のマップを作る
Map<String, Integer> nameLengthMap = names.stream()
    .collect(Collectors.toMap(
        name -> name,       // キー
        name -> name.length() // 値
    ));

System.out.println(nameLengthMap);
// {Alice=5, Bob=3, Charlie=7}

罠: キーが重複するとエラー

List<String> withDuplicate = List.of("Alice", "Alice", "Bob");

// キーが重複するとIllegalStateExceptionが発生!
Map<String, Integer> map = withDuplicate.stream()
    .collect(Collectors.toMap(
        name -> name,
        name -> name.length()
    ));

重複が起きうる場合は第3引数でマージ方法を指定します。

// 重複時は後勝ち(上書き)
Map<String, Integer> map = withDuplicate.stream()
    .collect(Collectors.toMap(
        name -> name,
        name -> name.length(),
        (existing, replacement) -> replacement // 重複時の処理
    ));

groupingBy() — グループ分けする

groupingBy() は要素をグループ化して Map<キー, List<要素>> にまとめます。

List<String> names = List.of("Alice", "Bob", "Anna", "Brian", "Charlie");

// 先頭文字でグループ化
Map<Character, List<String>> grouped = names.stream()
    .collect(Collectors.groupingBy(name -> name.charAt(0)));

System.out.println(grouped);
// {A=[Alice, Anna], B=[Bob, Brian], C=[Charlie]}
// グループごとの件数を数える
Map<Character, Long> countByInitial = names.stream()
    .collect(Collectors.groupingBy(
        name -> name.charAt(0),
        Collectors.counting()
    ));

System.out.println(countByInitial);
// {A=2, B=2, C=1}

DBのGROUP BYに相当する操作をJavaで行う時に便利です。


partitioningBy() — 2つに分類する

条件でtrueとfalseの2グループに分けます。

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Map<Boolean, List<Integer>> partitioned = numbers.stream()
    .collect(Collectors.partitioningBy(n -> n % 2 == 0));

System.out.println(partitioned.get(true));  // [2, 4, 6, 8, 10](偶数)
System.out.println(partitioned.get(false)); // [1, 3, 5, 7, 9](奇数)

「有効/無効」「合格/不合格」など2択の分類に使います。


counting() — 件数を数える

List<String> names = List.of("Alice", "Bob", "Anna", "Brian", "Charlie");

// 先頭文字ごとの件数
Map<Character, Long> count = names.stream()
    .collect(Collectors.groupingBy(
        name -> name.charAt(0),
        Collectors.counting()
    ));

System.out.println(count); // {A=2, B=2, C=1}

groupingBy() の第2引数として組み合わせて使うのが定番です。


まとめ

コレクター 用途
toList() リストにまとめる
toSet() 重複を除いてSetにまとめる
joining() 文字列として結合する
toMap() Mapに変換する(キー重複に注意)
groupingBy() 条件でグループ化する
partitioningBy() 2グループに分類する
counting() 件数を数える

おわりに

collect()toList() だけ覚えておけば最低限使えますが、groupingBy()joining() を知っているとforループで書いていた処理を短く書けるようになります。

「このループ、collectで書けるかも?」という視点でコードを見直してみてください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?