Java 逆引き集 | collect(Collectors.groupingBy(…)) — グルーピング集計

Java Java
スポンサーリンク

collect(Collectors.groupingBy(…)) — グルーピング集計

Stream API の collect(Collectors.groupingBy(...)) は「キーごとに要素をまとめる」ための強力な集約方法です。SQL の GROUP BY に近いイメージで、カテゴリ別集計や分類に役立ちます。


基本の仕組み

  • groupingBy(keyExtractor):
    • 要素から「キー」を取り出し、そのキーごとにリストへまとめる。
    • 戻り値は Map<K, List<T>>
  • groupingBy(keyExtractor, downstreamCollector):
    • キーごとに「さらに集約」を行う。
    • 例: 件数を数える、合計を出す、セットにするなど。

基本コード例

1) 単純なグルーピング

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

public class GroupingBasic {
    public static void main(String[] args) {
        List<String> words = List.of("apple","banana","apricot","blueberry","cherry");

        Map<Character, List<String>> grouped =
            words.stream()
                 .collect(Collectors.groupingBy(w -> w.charAt(0)));

        System.out.println(grouped);
        // {a=[apple, apricot], b=[banana, blueberry], c=[cherry]}
    }
}
Java
  • ポイント: 先頭文字をキーにして分類。

2) 件数を数える(downstream に counting)

List<String> words = List.of("apple","banana","apricot","blueberry","cherry");

Map<Character, Long> counts =
    words.stream()
         .collect(Collectors.groupingBy(w -> w.charAt(0),
                                        Collectors.counting()));

System.out.println(counts);
// {a=2, b=2, c=1}
Java
  • ポイント: Collectors.counting() を使うとキーごとの件数が得られる。

3) 合計値を出す(summingInt)

record Product(String category, int price) {}

List<Product> products = List.of(
    new Product("fruit",100),
    new Product("fruit",200),
    new Product("veg",150),
    new Product("veg",120)
);

Map<String, Integer> sumByCategory =
    products.stream()
            .collect(Collectors.groupingBy(Product::category,
                                           Collectors.summingInt(Product::price)));

System.out.println(sumByCategory);
// {fruit=300, veg=270}
Java
  • ポイント: summingInt で数値フィールドを合計。

4) セットにまとめる(toSet)

List<String> words = List.of("apple","banana","apricot","blueberry","cherry");

Map<Character, Set<String>> grouped =
    words.stream()
         .collect(Collectors.groupingBy(w -> w.charAt(0),
                                        Collectors.toSet()));

System.out.println(grouped);
// {a=[apple, apricot], b=[banana, blueberry], c=[cherry]}
Java
  • ポイント: リストではなく Set にすれば重複排除。

例題で理解する

例題1: 学生を「合否」で分類

record Student(String name, int score) {}

List<Student> students = List.of(
    new Student("Tanaka",80),
    new Student("Sato",55),
    new Student("Ito",90)
);

Map<String, List<Student>> grouped =
    students.stream()
            .collect(Collectors.groupingBy(s -> s.score >= 60 ? "Pass" : "Fail"));

System.out.println(grouped);
// {Pass=[Tanaka, Ito], Fail=[Sato]}
Java

例題2: 社員を部署ごとに人数集計

record Employee(String dept, String name) {}

List<Employee> emps = List.of(
    new Employee("Sales","A"),
    new Employee("Sales","B"),
    new Employee("IT","C"),
    new Employee("IT","D"),
    new Employee("HR","E")
);

Map<String, Long> deptCounts =
    emps.stream()
        .collect(Collectors.groupingBy(Employee::dept, Collectors.counting()));

System.out.println(deptCounts);
// {Sales=2, IT=2, HR=1}
Java

例題3: 商品をカテゴリ別に平均価格

Map<String, Double> avgByCategory =
    products.stream()
            .collect(Collectors.groupingBy(Product::category,
                                           Collectors.averagingInt(Product::price)));

System.out.println(avgByCategory);
// {fruit=150.0, veg=135.0}
Java

テンプレート集

  • 基本分類
Map<K, List<T>> map = stream.collect(Collectors.groupingBy(x -> key));
Java
  • 件数集計
Map<K, Long> map = stream.collect(Collectors.groupingBy(x -> key, Collectors.counting()));
Java
  • 合計集計
Map<K, Integer> map = stream.collect(Collectors.groupingBy(x -> key, Collectors.summingInt(x -> value)));
Java
  • 平均集計
Map<K, Double> map = stream.collect(Collectors.groupingBy(x -> key, Collectors.averagingInt(x -> value)));
Java
  • セット化
Map<K, Set<T>> map = stream.collect(Collectors.groupingBy(x -> key, Collectors.toSet()));
Java

落とし穴と回避策

  • キーが null: groupingBy は null キーも許すが Map 実装に依存。避けるか事前に処理。
  • 大量データ: distinct や groupingBy は全件保持するためメモリ負荷が大きい。必要なら DB 側で集計。
  • equals/hashCode: キーの判定は equals/hashCode に依存。独自型は正しく実装する。
  • 結果の Map 型: デフォルトは HashMap。順序が必要なら groupingBy(..., LinkedHashMap::new, downstream) を使う。

まとめ

  • collect(Collectors.groupingBy(...)) は「キーごとに分類・集計」する便利な仕組み。
  • downstream を組み合わせると件数・合計・平均・セット化など多彩な集約が可能。
  • SQL の GROUP BY と同じ感覚で使えるので、データ処理やレポート生成に役立つ。

👉 練習課題: 「社員リストを部署ごとに分類し、部署ごとの平均年齢を求める」コードを書いてみると、groupingBy の力が実感できます。

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