스트림 API 컬렉터
스트림이 중간 연산을 거쳐 최종 연산으로써 데이터를 처리할 때, 그 결과물이 필요한 경우가 많다. 이 최종 연산에 Collectors를 활용한다.
collect 연산은 반환값을 만들어내는 최종 연산이다. Collectors 클래스 안에 준비된 여러 메서드를 통해서 다양한 수집 방식을 적용할 수 있다. 필요한 대부분의 기능이 Collectors에 이미 구현되어 있기 때문에, Collector 인터페이스를 직접 구현하는 것보다는 Collectors의 사용법을 익히는 것이 중요하다.
기본적인 수집
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Stream;
import static java.util.stream.Collectors.*;
public class Collectors1Basic {
public static void main(String[] args) {
//기본 기능
List<String> list = Stream.of("Java", "Spring", "JPA").collect(toList());
//수정 불가능 리스트
List<Integer> unmodifiableList = Stream.of(1, 2, 3).collect(toUnmodifiableList());
// unmodifiableList.add(4); //예외 발생
Set<Integer> set = Stream.of(1, 2, 2, 3, 3, 3).collect(toSet());
Set<Integer> unmodifiableSet = Stream.of(1, 2, 2, 3, 3, 3).collect(toUnmodifiableSet());
//타입 지정
TreeSet<Integer> treeSet = Stream.of(3, 4, 5, 2, 1).collect(toCollection(TreeSet::new));
}
}Collectors 클래스에는 스트림을 다양한 컬렉션으로 수집할 수 있는 API들이 정의되어 있다. 원하는 컬렉션 구현체를 직접 지정할 수도 있으며, static import를 사용할 수 있다.
Map 수집
그룹과 분할 수집
최솟값, 최댓값 수집
Collectors.maxBy()와Collectors.minBy()를 통해 최소, 최댓값을 구할 수 있다.다만 스트림 자체가 제공하는
max(),min()메서드를 쓰면 더 간단하다.기본형 특화 스트림을 쓰면
max()처럼 바로 결과를 얻을 수 있다.Collectors의 일부 기능은 스트림에서 직접 제공하는 기능과 중복되며, 다운 스트림 컬렉터에서 유용하게 사용할 수 있다.
통계 수집
Collectors의 일부 기능은 스트림에서 직접 제공하는 기능과 중복되며, 다운 스트림 컬렉터에서 유용하게 사용할 수 있다.
리듀싱 수집
Collectors.reducing()은 최종적으로 하나의 값으로 요소들을 합치는 방식을 지정한다. 스트림 자체의reduce()메서드와 유사한 기능이다.문자열을 이어붙일 때는
Collectors.joining()또는String.join()을 쓰는 게 더 간편하다.Collectors의 일부 기능은 스트림에서 직접 제공하는 기능과 중복되며, 다운 스트림 컬렉터에서 유용하게 사용할 수 있다.
다운 스트림 컬렉터
다운 스트림 컬렉터란?
Collectors.groupingBy()또는Collectors.partitioningBy()에서 두번째 인자로 전달되는Collector를 가리켜 다운 스트림 컬렉터라 한다.예를 들어
Collectors.groupingBy(classifier, downstreamCollector)형태로 사용될 때,downstreamCollector는classifier에 의해 분류된 각 그룹 내부의 요소들을 다시 한번 어떻게 처리할지를 정의하는 역할을 한다.만약 다운 스트림 컬렉터를 명시하지 않으면 기본적으로
Collectors.toList()가 적용되어 그룹별 요소들을 List로 모은다.그룹별 개수를 세거나, 평균을 구하거나, 특정 필드를 뽑아서 맵핑하는 등의 작업이 필요하다면 적절한 다운 스트림 컬렉터를 추가로 지정해야 한다.
정리하면 다운 스트림 컬렉터는 그룹화(또는 분할)를 먼저 한 뒤, 각 그룹(또는 파티션) 내부의 요소들을 어떻게 처리할 것인가를 지정하는 데 사용된다.
다운 스트림 컬렉터가 필요한 이유
groupingBy()를 사용하면 일단 요소가 그룹별로 묶이지만, 그룹 내 요소를 어떻게 처리할지는 기본적으로toList()만 적용된다.하지만 보통 "그룹별 총합, 평균, 최대, 최소, 매핑된 결과, 통계" 등을 바로 얻고 싶을 때가 많다.
다운 스트림 컬렉터는 그룹화된 이후 그룹 내부에서 추가적인 연산 또는 결과물을 정의하는 역할을 한다.
즉 다운 스트림 컬렉터를 사용하면 그룹 내부를 다시 한번 모으거나 집계하여 원하는 결과를 얻을 수 있다.
다음은 Student 클래스를 각 학년별로 그룹화한 다음, 그룹화한 학년별 점수의 합을 구하는 예시이다.

다운 스트림 컬렉터 예제 - 1
다운 스트림 컬렉터 - toList()

다운 스트림 컬렉터 - mapping()

다운 스트림 컬렉터 - 집계

다운 스트림 컬렉터 예제 - 2
다운 스트림 컬렉터 - reduce()

👆 mapping() vs collectingAndThen()
mapping(): 그룹화(또는 분할)된 각 그룹 내의 개별 요소들을 다른 값으로 변환한 뒤, 그 변환된 값들을 다시 다른 Collector로 수집할 수 있게 해준다.
주된 목적 : 그룹 내 개별 요소를 변환한 뒤, 해당 변환 결과를 다른 Collector로 수집
처리 방식 : 그룹화 → 각 요소를 변환(mapping) → List나 Set 등으로 수집
collectingAndThen(): 다운 스트림 컬렉터가 최종 결과를 만든 뒤에 한 번 더 후처리할 수 있도록 해준다. 즉 1차 Collector → 후처리 함수 순서로 작업한다.
주된 목적 : 그룹 내 요소들을 이미 한번 수집한 결과를 추가 가공하거나 최종 타입으로 변환
처리 방식 : 그룹화 → 최댓값/최솟값/합계 등 수집(collecting) → 결과를 후처리(AndThen)
mapping()은 그룹화된 요소 하나하나를 변환하는 데 유용하고,collectingAndThen()은 이미 만들어진 전체 그룹의 결과를 최종 한번 더 작업하는 데 사용한다.
Last updated