5-1 컬렉터린 무엇인가?
→ Collector 인터페이스 구현은 스트림의 요소를 어떤 식으로 도출할지 지정한다. / collect로 결과를 수집하는 과정을 간단하고 유연한 방식으로 정의할 수 있다는 점이 장점
보통 함수를 요소로 변환할 때는 컬렉터를 적용하며 최종결과를 저장하는 자료구조에 값을 누적한다.
미리 정의된 컬렉터
- 스트림 요소를 하나의 값으로 리듀스하고 요약
- 요소 그룹화
- 요소 분할
5-2 리듀싱과 요약
→ 컬렉터로 스트림의 모든 항목을 하나의 결과로 합칠 수 있다. 트리를 구성하는 다수준 맵, 메누의 말로리 합계를 가리키는 단순한 정수 등 다양한 형식으로 결과가 도출될 수 있다.
long howManyDishes = menu.stream().collect(Collectors.counting());
long howManyDishes = menu.stream().count(); //java.util.stream.Collectors.*;를 임포트했다고 가정
(1) 요약 연산
→ Collector 클래스는 Collector.summingInt라는 요약 펙토리 메서드를 제공한다. 이는 객체를 int로 매핑하는 함수를 인수로 받고, 객체를 int로 매핑한 컬텍터를 반환한다.
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories)); //칼로리가 매핑된 각 요리의 값을 탐색하면서 설정되어있는 누적자에 칼로리를 더한다.
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories)); //IntSummaryStatistics 클래스로 모든 정보가 수집된다.(count, sum, min, max, average)
(2) 문자열 연결
→ 컬렉터에 joining 팩토리 메서드를 이용하면 스트림의 각 객체에 toString 메서드를 호출해서 추출한 모든 문자열을 하나의 문자열로 연결해서 반환한다.
String shortMenu = menu.stream().collect(joining()); //구분자 없이 모든 문자열이 연결되어 반환
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", ")); //", "로 문자열 구분
(3) 범용 리듀싱 요약 연산
→ Collectors.reducing으로 정의 / 범용 팩토리 대신 특화된 컬렉터를 사용하는 이유 : 프로그래밍적 편의성, 가독성 향상
reducing의 인수
- 첫 번째 : 리듀싱의 연산의 시작값이나 스트림에 인수가 없을 때는 반환값
- 두 번째 : 변환할 때 사용하는 변환함수
- 세 번째 : 같은 종류의 두 항목을 하나의 값으로 더하는 BinaryOperator
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));
* collect와 reduce
→ collect 메서드 : 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드 / reduce : 두 값을 하나로 도출하는 불변형 연산
* 제네릭 와일드카드 ' ? ' 사용법
→ ?는 누적자 형식이 알려지지 않았음을, 즉 누적자의 형식이 자유로움을 의미한다.
5-3 그룹화
분류함수 : 그룹화의 기준이 되는 함수
→ 그룹화 연산의 결과로 그룹화 함수가 반환하는 키 그리고 각 키에 대응하는 스트림의 모든 항목 리스트를 값으로 갖는 맵이 반환된다.
그룹화된 요소 조작
팩토리 메서드 Collector.groupingBy 를 이용해서 항목을 그룹화할 수 있다.
Map<Dish.Type, List<Dish>> caloricDishesByType = menu.stream().filter(dish.getCalories()>500)
.collect(groupingBy(Dish::getType)); //결과가 없으면 키값도 지워짐
Map<Dish.Type, List<Dish>> caloricDishesByType = menu.stream()
.collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList()))); //결과가 없어도 키값이 살아있음
다수준 그룹화
두 인수를 받는 팩토리 메서드 Collector.groupingBy 를 이용해서 항목을 다수준으로 그룹화할 수 있다.
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishsByTypeCaloricLevel =
menu.stream().collect(
groupingBy(Dish::getType,
groupingBy(dish -> {
if (dish.getCalories() <= 400) //Type별로 그룹화
return CaloricLevel.DIET; //칼로리별로 그룹화
else if (dish.getCalories() <= 700)
return CaloricLevel.NORMAL;
else
return CaloricLevel.FAT;
})
)
);
서브그룹으로 데이터 수집
한 개의 인수를 갖는 groupingBy(f)는 groupingBy(f, toList())의 축약형이다.
Map<Dish.Type, Optional<Dish>> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
maxBy(comparingInt(Dish::getCalories)))); //Optional<Dish>는 해당 종류의 음식 중 가장 높은 칼로리를 래핑한다.
'Modern java in action' 카테고리의 다른 글
5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.08.22 |
---|---|
[modern java in action] stream(4) (0) | 2021.06.24 |
[modern java in action] stream(3) (0) | 2021.06.24 |
[Modern java in action] Stream(2) (0) | 2021.06.24 |
[modern java in action] Stream(1) (0) | 2021.06.24 |
댓글