Java함수형과 현대 Java

Stream Collectors, groupingBy, toMap

Stream의 마지막 단계에서 결과를 어떤 컬렉션 모양으로 모을지 결정하는 `Collectors`와 `groupingBy`, `toMap` 패턴을 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

java
Map<String, List<Student>> byDepartment =
        students.stream()
                .collect(Collectors.groupingBy(Student::getDepartment));

설명

  • Stream에서 terminal operation은 단순히 끝내는 단계가 아니라, 결과의 모양을 결정하는 단계이기도 합니다. 이 역할을 가장 많이 맡는 것이 Collectors입니다.
  • groupingBy는 분류 기준에 따라 여러 값을 묶어 Map<K, List<T>> 형태를 만들고, 필요하면 downstream collector를 붙여 counting, mapping, averagingInt 같은 추가 집계를 연결할 수 있습니다.
  • toMap은 한 항목을 key-value로 변환해 맵을 만들 때 쓰지만, key 충돌이 날 수 있다는 점이 중요합니다. 충돌 정책 없이 무심코 쓰면 런타임 예외가 날 수 있습니다.
  • 이 카드의 핵심은 "수집의 설계"입니다. Stream 중간 단계가 데이터를 어떻게 흘려 보낼지 정한다면, collector는 마지막에 어떤 구조로 정착시킬지 결정합니다.
  • 좋은 Stream 코드는 filter-map까지만 깔끔한 것이 아니라, 마지막 수집 구조까지 도메인에 잘 맞아야 합니다. 결국 리포트, 인덱싱, lookup map, 그룹 집계가 여기서 결정됩니다.

빠른 정리

collector역할
toList()리스트로 수집
toSet()집합으로 수집
groupingBy(...)키 기준 그룹화
toMap(...)맵으로 수집
downstream collector그룹 결과를 추가 집계

주의할 점

toMap은 key가 겹칠 수 있는 데이터에 그대로 쓰면 예외가 날 수 있습니다. key 충돌 가능성이 있다면 merge 함수가 필요한지 먼저 생각하는 편이 안전합니다.

참고 링크

2 sources