본문 바로가기
language/java

Java Collector 활용 완벽 이해하기

by 죄니안죄니 2026. 5. 28.
반응형

Java Collector 활용 완벽 이해하기

Java Stream API를 사용하다 보면 거의 반드시 등장하는 것이 바로:

Collector

입니다.

특히 실무에서는:

  • 리스트 변환
  • 그룹핑
  • 통계 계산
  • Map 변환
  • 데이터 집계

전부 Collector 기반으로 처리하는 경우가 많습니다.

예:

 
list.stream()
    .collect(Collectors.toList());
 

이 코드가 바로 Collector 사용 예시입니다.

처음에는 단순히:

Stream 결과를 List로 바꾸는 거?
 

처럼 보이지만,

실제로 Collector는:

  • Stream 최종 수집 전략
  • mutable reduction
  • grouping
  • downstream collector
  • 병렬 Stream 수집

까지 연결되는 매우 중요한 Stream 핵심 개념입니다.

이번 글에서는:

  • Collector란?
  • collect() 동작 원리
  • toList()
  • groupingBy()
  • partitioningBy()
  • mapping()
  • downstream collector
  • 병렬 수집 구조

까지 깊게 정리해보겠습니다.


1. Collector란?

Collector 는:

Stream 데이터를 원하는 형태로 수집하는 전략 객체

입니다.


2. 왜 필요할까?

Stream은 기본적으로:

데이터 흐름(pipeline)
 

입니다.

즉 마지막에:

결과를 어디에 담을지
 

결정 필요.


3. collect()란?

대표 최종 연산.

 
.collect(...)
 

4. 역할

Stream 결과를 수집
 

합니다.


5. 가장 기본 예시

 
List<String> result =
    stream.collect(Collectors.toList());
 

6. 의미

Stream 요소들을 List로 모음
 

입니다.


7. Collectors 클래스

Collectors 는:

다양한 Collector 생성 유틸 클래스

입니다.


8. 가장 많이 사용하는 Collector

Collector역할
toList List 수집
toSet Set 수집
toMap Map 수집
groupingBy 그룹핑
partitioningBy 분할
joining 문자열 결합
counting 개수 계산

9. toList()

가장 기본.

 
.collect(Collectors.toList())
 

10. 동작

Stream → List 변환
 

입니다.


11. 예시

 
List<Integer> result =
    list.stream()
        .filter(x -> x > 10)
        .collect(Collectors.toList());
 

12. toSet()

 
.collect(Collectors.toSet())
 

13. 특징

중복 제거
 

가능.


14. joining()

문자열 결합.


15. 예시

 
String result =
    list.stream()
        .collect(Collectors.joining(", "));
 

16. 결과

A, B, C
 

형태.


17. counting()

개수 계산.


18. 예시

 
long count =
    list.stream()
        .collect(Collectors.counting());
 

19. 사실상 count()와 유사

하지만 downstream collector에서 매우 중요.


20. groupingBy()

실무에서 매우 중요합니다.


21. 의미

특정 기준으로 그룹화
 

입니다.


22. 예시

 
Map<String, List<User>> result =

users.stream()
     .collect(
         Collectors.groupingBy(
             User::getDepartment
         )
     );
 

23. 결과 구조

부서별 User 목록
 

Map 생성.


24. 예시 결과

{
  "DEV": [...],
  "HR": [...]
}
 

25. groupingBy 내부 동작

실제로는:

key 생성
↓
Map bucket 저장
↓
List 누적
 

구조.


26. partitioningBy()

groupingBy와 유사하지만:

boolean 기준 2분할
 

전용.


27. 예시

 
Map<Boolean, List<Integer>> result =

list.stream()
    .collect(
        Collectors.partitioningBy(
            x -> x > 10
        )
    );
 

28. 결과

true -> 조건 만족
false -> 조건 불만족
 

29. toMap()

매우 중요.


30. 예시

 
Map<Long, User> map =

users.stream()
     .collect(
         Collectors.toMap(
             User::getId,
             user -> user
         )
     );
 

31. 의미

id 기반 Map 생성
 

입니다.


32. 그런데 주의점

매우 중요.

중복 key 발생 시:

IllegalStateException
 

발생 가능.


33. 해결 방법

merge function 추가.

 
Collectors.toMap(
    User::getId,
    user -> user,
    (a, b) -> a
)
 

34. downstream collector란?

매우 중요합니다.


35. 의미

Collector 내부에 또 Collector 사용
 

하는 것.


36. 예시

 
Collectors.groupingBy(
    User::getDepartment,
    Collectors.counting()
)
 

37. 결과

부서별 인원 수
 

Map 생성.


38. mapping()

downstream에서 자주 사용.


39. 예시

 
Collectors.groupingBy(
    User::getDepartment,
    Collectors.mapping(
        User::getName,
        Collectors.toList()
    )
)
 

40. 결과

부서별 이름 목록
 

생성.


41. collectingAndThen()

후처리 가능.


42. 예시

 
Collectors.collectingAndThen(
    Collectors.toList(),
    Collections::unmodifiableList
)
 

43. 의미

수집 후 immutable 변환
 

입니다.


44. Collector 내부 구조

실제로 Collector는 꽤 복잡.

핵심 구성:

구성역할
supplier 결과 container 생성
accumulator 데이터 누적
combiner 병렬 결과 병합
finisher 최종 변환

45. 왜 combiner 필요할까?

병렬 Stream 때문.


46. parallelStream()

 
parallelStream()
 

에서는:

부분 결과 여러 개 생성
 

가능.


47. 그래서 combiner 등장

부분 결과 병합
 

수행.


48. mutable reduction

collect는 사실상:

mutable reduction
 

입니다.

즉:

가변 객체에 누적
 

하는 방식.


49. reduce와 차이

연산특징
reduce immutable 누적
collect mutable 수집

50. 실무에서 많이 쓰는 패턴

대표:


DTO 그룹핑

 
groupingBy(User::getTeam)
 

통계 계산

 
averagingInt(...)
summingInt(...)
 

문자열 조합

 
joining(",")
 

51. 성능 주의점

groupingBy는:

Map 생성 비용
 

존재.

대용량 데이터 시 메모리 사용 증가 가능.


52. 병렬 Stream 주의점

Collector가:

thread-safe 보장 안 하면
 

병렬 처리 문제 가능.


53. groupingByConcurrent()

병렬 최적화용 존재.

 
Collectors.groupingByConcurrent(...)
 

54. 실무에서 자주 하는 실수

1) toMap 중복 key 미처리

IllegalStateException 발생.


2) groupingBy 남용

메모리 사용 증가 가능.


3) 병렬 Stream + mutable 객체 사용

동시성 문제 가능.


4) collect와 reduce 차이 이해 부족

mutable/immutable 차이 중요.


55. 핵심 흐름 요약

Stream
↓
Collector 전달
↓
데이터 누적(accumulate)
↓
필요 시 병합(combine)
↓
최종 결과 반환
 

56. 가장 중요한 핵심 한 줄

Collector는
“Stream 결과를 원하는 구조로 수집하는 전략 객체”
 

입니다.


57. 정리

Collector는 단순 List 변환 API가 아닙니다.

실제로는:

  • Stream Pipeline
  • mutable reduction
  • grouping
  • 병렬 수집
  • downstream collector

전체와 연결되는 Stream 핵심 수집 메커니즘입니다.

특히 실무에서는:

  • groupingBy
  • toMap
  • downstream collector
  • collectingAndThen
  • 병렬 collect 구조

를 정확히 이해하는 것이 매우 중요합니다.

다음 글에서는:

Optional<T> 사용법

을 NullPointerException 방지, map/flatMap/orElse 차이, Optional 남용 문제까지 포함해서 깊게 정리해보겠습니다.

반응형

댓글