collect の役割をざっくりイメージする
Stream の collect は一言でいうと、
「ストリームの“流れ”を、最終的な入れ物や形(List・Set・Map・1つの値など)にまとめる操作」
です。
filter や map はまだ「途中の加工」です。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]
}
}
Javacollect(Collectors.toList()) は、
「ストリームの要素を、List に“集め直す”」というお決まりの形です。
流れを分解すると、
names.stream()
→ filter で3文字以下だけに絞る
→ collect(toList) で「残った要素を List に詰め直す」
ということをしています。
なぜ toList が必要なのか
filter や map の結果はまだ 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] など(順序は保証されない)
}
}
JavatoSet() は「順番は気にしないから、とにかく一意な集合にしたい」時に使います。
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
}
}
Javajoining(", ") は、「区切り文字付きで全部つなげる」Collector です。
シグネチャとしては、区切り・prefix・suffix も指定できます。
String joined =
names.stream()
.collect(Collectors.joining(", ", "[", "]"));
// [Alice, Bob, Carol]
JavaStream の結果を 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
}
}
JavaCollectors.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@...]}
}
}
JavagroupingBy(s -> s.subject) は、
「subject をキーにして、同じ subject の要素を List にまとめる Map」
を作ってくれます。
さらに、「グループごとの別集計」も組み合わせられます。
例えば科目ごとに合計点を出したいなら:
Map<String, Integer> totalBySubject =
scores.stream()
.collect(Collectors.groupingBy(
s -> s.subject,
Collectors.summingInt(s -> s.value)
));
JavagroupingBy(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が主役

