Java | Java 標準ライブラリ:collect

Java Java
スポンサーリンク

collect の役割をざっくりイメージする

Stream の collect は一言でいうと、

「ストリームの“流れ”を、最終的な入れ物や形(List・Set・Map・1つの値など)にまとめる操作

です。

filtermap はまだ「途中の加工」です。
collect は「終点」で、

  • List や Set、Map に「集め直す」
  • 文字列を 1 本につなげる
  • 合計・平均・グルーピングなどの“集計結果”にまとめる

といった「結果物」を作るために使います。

「Stream でいろいろ流したあと、最後どう形にするか」を担当するのが collect と覚えてください。


基本中の基本:toList で「List に戻す」

最もよく使う形:Collectors.toList()

典型例からいきます。

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

public class CollectToListBasic {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Carol", "Dan");

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

        System.out.println(shortNames); // [Bob, Dan]
    }
}
Java

collect(Collectors.toList()) は、
「ストリームの要素を、List に“集め直す”」というお決まりの形です。

流れを分解すると、

names.stream()
→ filter で3文字以下だけに絞る
→ collect(toList) で「残った要素を List に詰め直す」

ということをしています。

なぜ toList が必要なのか

filtermap の結果はまだ Stream<T> であって、List ではありません。
「また別の処理に渡したい」「メソッドの戻り値にしたい」といった場面では、
最終的にコレクションに戻してあげる必要があります。

その役目を担うのが collect(Collectors.toList()) です。


collect の正体:Collector を使った「まとめ方の指示」

collect のシグネチャをざっくり眺める

よく使う形はこちらです。

<R, A> R collect(Collector<? super T, A, R> collector)
Java

難しそうに見えますが、ざっくり言い換えると、

T のストリームを、R という形にまとめる“やり方(Collector)”を渡すと、
その通りにまとめて R を返してくれる」

というメソッドです。

その「やり方」をまとめて用意してくれているのが java.util.stream.Collectors クラスで、
ここに

  • toList()
  • toSet()
  • joining()
  • groupingBy(...)
  • summingInt(...)

など、便利な“集め方テンプレート”がたくさん定義されています。

つまり、

「collect は何でもできる器」
「Collectors は“よくあるまとめ方”のカタログ」

という関係です。


代表的な Collectors を具体例で押さえる

toSet:重複を除いた集合にまとめる

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

public class CollectToSet {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Alice", "Carol");

        Set<String> uniqueNames =
                names.stream()
                     .collect(Collectors.toSet());

        System.out.println(uniqueNames); // [Alice, Bob, Carol] など(順序は保証されない)
    }
}
Java

toSet() は「順番は気にしないから、とにかく一意な集合にしたい」時に使います。

toList / toSet の違いは、

  • 順序・重複を保ちたい → List
  • 重複を消したい → Set

という感覚で使い分けるとよいです。

joining:文字列を 1 本につなげる

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

public class CollectJoining {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Carol");

        String joined =
                names.stream()
                     .collect(Collectors.joining(", "));

        System.out.println(joined); // Alice, Bob, Carol
    }
}
Java

joining(", ") は、「区切り文字付きで全部つなげる」Collector です。

シグネチャとしては、区切り・prefix・suffix も指定できます。

String joined =
    names.stream()
         .collect(Collectors.joining(", ", "[", "]"));
// [Alice, Bob, Carol]
Java

Stream の結果を 1 つの文字列にしたい場面でよく使われます。

summingInt:合計を Collector で出す

mapToInt(...).sum() でも合計は出せますが、collect ベースで書くこともできます。

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

public class CollectSumming {
    public static void main(String[] args) {
        List<Integer> scores = Arrays.asList(80, 90, 70);

        int total =
                scores.stream()
                      .collect(Collectors.summingInt(x -> x));

        System.out.println(total); // 240
    }
}
Java

Collectors.summingInt は、「T から int を取り出して合計する」という Collector です。
Employee::getSalary のようにメソッド参照を渡すこともよくあります。


もう一歩:groupingBy / partitioningBy で「グルーピング」する

groupingBy:キーごとのグループにまとめる

groupingBy は、「あるキーでグループ分けした Map を作る」ための Collector です。

例として、「科目別に点数をグルーピングする」イメージで書いてみます。

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

class Score {
    String subject;
    int value;
    Score(String subject, int value) {
        this.subject = subject;
        this.value = value;
    }
}

public class CollectGroupingBy {
    public static void main(String[] args) {
        List<Score> scores = List.of(
                new Score("Math", 80),
                new Score("Math", 90),
                new Score("English", 70),
                new Score("English", 85)
        );

        Map<String, List<Score>> bySubject =
                scores.stream()
                      .collect(Collectors.groupingBy(s -> s.subject));

        System.out.println(bySubject);
        // 例: {Math=[Score@..., Score@...], English=[Score@..., Score@...]}
    }
}
Java

groupingBy(s -> s.subject) は、

「subject をキーにして、同じ subject の要素を List にまとめる Map」

を作ってくれます。

さらに、「グループごとの別集計」も組み合わせられます。
例えば科目ごとに合計点を出したいなら:

Map<String, Integer> totalBySubject =
        scores.stream()
              .collect(Collectors.groupingBy(
                      s -> s.subject,
                      Collectors.summingInt(s -> s.value)
              ));
Java

groupingBy(keyExtractor, downstreamCollector) という形で、
「グルーピングしたあとに何をするか」も指定できます。

partitioningBy:true / false の 2 グループに分割

partitioningBy は、「条件に合う / 合わない」の 2 グループに分ける Collector です。

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

public class CollectPartitioningBy {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

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

        System.out.println(partitioned);
        // {false=[1, 3, 5], true=[2, 4, 6]}
    }
}
Java

キーが true / false の Map になり、

true 側に条件を満たす要素
false 側にそれ以外

が入ります。

「ログをエラーかどうかで分ける」「ユーザーを条件で 2 つに分ける」などに使えます。


collect と toList()(Stream のメソッド)の違い

Java 16 以降の Stream#toList と何が違う?

Java 16 以降は、Stream 自身に toList() メソッドが追加されました。

List<String> list = stream.toList();
Java

これは collect(Collectors.toList()) とほぼ同じ用途ですが、
いくつかの細かい違いがあります(返される List が変更可能かどうかなど)。

初心者のうちは、

シンプルに List にするだけ → stream.toList()(Java 16+)
Collectors と組み合わせた複雑な集計 → collect(...)

と使い分けておけば十分です。

collect は「まとめ方を自由にカスタムできる強力な終端操作」
toList() は「ただ List にしたいだけの簡易ショートカット」

というイメージで捉えておくと、頭が整理しやすくなります。


まとめ:collect を自分の中でどう位置づけるか

collect を初心者向けに一言でまとめると、

Stream の流れを、List / Set / Map / 文字列 / 集計結果など、具体的な形に“集めて終わらせる”ための終端操作

です。

特に押さえておきたいポイントは次の通りです。

  • collect(Collectors.toList()) で「List に戻す」のが最頻出
  • toSet / joining / summingInt / groupingBy / partitioningBy など、Collectors は“まとめ方テンプレ”のカタログ
  • collect は「終端操作」で、ここを呼んだタイミングでストリーム処理が実行され、ストリームは消費される
  • Java 16 以降は単純な List 変換なら stream.toList() でも書けるが、集計やグルーピングには依然として collect が主役

タイトルとURLをコピーしました