Collectors 클래스는 Stream API의 핵심 구성 요소입니다. Java 9부터 이 클래스에 새로운 메서드인 filtering()이 추가되어, 다운스트림 수집(collect) 과정에서 스트림 요소를 필터링할 수 있게 되었습니다.
스트림에서 제공하는 filter() 메서드와 비슷해 보이지만, 둘 사이에는 중요한 차이점이 있습니다. 스트림의 filter()는 그룹화(grouping)가 일어나기 전에 값을 미리 걸러내는 반면, Collectors.filtering()은 Collectors.groupingBy()와 함께 사용할 경우 값들이 먼저 그룹화된 후 각 그룹 내부에서 필터링을 수행합니다. 이 덕분에 특정 조건에 맞지 않아 요소가 모두 걸러진 그룹이라도 그룹 키 자체는 결과에 유지될 수 있습니다.
문법(Syntax)
public static <T, A, R> Collector<T, ?, R> filtering(
Predicate<? super T> predicate,
Collector<? super T, A, R> downstream)
- predicate: 각 요소에 적용할 필터 조건
- downstream: 조건을 통과한 요소를 전달받아 실제 수집을 수행하는 Collector
예제 코드
다음 예제는 문자열 리스트를 길이별로 그룹화하면서, 각 그룹 안에서 문자 'z'를 포함하지 않는 요소만 남기는 코드입니다.
import java.util.stream.*;
import java.util.*;
public class FilteringMethodTest {
public static void main(String args[]) {
List<String> list = List.of("x", "yy", "zz", "www");
Map<Integer, List<String>> result = list.stream()
.collect(Collectors.groupingBy(String::length,
Collectors.filtering(s -> !s.contains("z"),
Collectors.toList())));
System.out.println(result);
}
}
실행 결과
{1=[x], 2=[yy], 3=[www]}
결과 분석
길이가 2인 그룹에는 원래 "zz"와 "yy" 두 요소가 속해 있었지만, 'z'를 포함하는 "zz"가 필터링되어 "yy"만 남은 것을 확인할 수 있습니다.
만약 여기서 스트림의 filter() 메서드를 대신 사용했다면 결과가 달라질 수 있습니다. filter()는 그룹화 이전에 요소를 제거하므로, 어떤 그룹에 속한 모든 요소가 조건에 맞지 않으면 해당 그룹 키 자체가 결과에서 사라집니다. 반면 filtering()은 그룹화가 먼저 이루어지기 때문에, 요소가 모두 걸러진 그룹도 빈 컬렉션 형태로 결과에 유지됩니다. 따라서 "그룹 구조는 유지하면서 그룹 내부의 내용만 필터링하고 싶은 경우"에 Collectors.filtering()이 매우 유용합니다.