컬렉터로 스트림의 모든 항목을 하나의 결과로 합칠 수 있다.
long howManyDishes = menu.stream().count();
Comparator<Dish> dishCaloriesComparator =
Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream()
.collect(maxBy(dishCaloriesComparator));
스트림에 있는 객체의 숫자 필드의 합계나 평균 등을 반환하는 연산에도 리듀싱 기능이 자주 사용된다.
int totalCalories = menu.stream()
.collect(summingInt(Dish::getCalories)); // 합계
double avgCalories = menu.stream()
.collect(averagingInt(Dish::getCalories)); // 평균
IntSummaryStatistics menuStatistics = menu.stream()
.collect(summarizingInt(Dish::getCalories)); // 요소 수, 합계, 평균, 최댓값, 최솟값
joining 메서드는 내부적으로 StringBuilder를 이용해서 문자열을 하나로 만든다.
String shotMenu = menu.stream()
.map(Dish::getName)
.collect(joining());
String shotMenu = menu.stream()
.map(Dish::getName)
.collect(joining(", )); // ,를 구분자로 출력
이전에 살펴봤던 모든 컬렉터는 reducing 팩토리 메서드로도 정의할 수 있다. 그럼에도 불구하고 특화된 컬렉터를 사용한 이유는 프로그래밍적 편의성 때문이다.
int totalCalories = menu.stream()
.collect(reducing(0, Dish::getCalories, (i, j) -> i + j));
한 개의 인수를 가진 reducing 버전을 이용해서 가장 칼로리가 높은 요리를 찾는 방법도 있다.
Optional<Dish> mostCalorieDish =
menu.stream().collect(reducing(
(d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));
Map<Dish.Type, List<Dish>> dishesByType =
menu.stream().collect(groupingBy(Dish::getType));
{
FISH=[prawns, salmon],
OTHER=[french fries, rice, season fruit, pizza],
MEAT=[pork, beef, chicken]
}
Map<Dish.Type, List<Dish>> caloricDishesByType = menu.stream().collect(
groupingBy(Dish::getType,
filtering(dish -> dish.getCalories() > 500, toList())));
}
{
OTHER=[french fries, pizza],
MEAT=[pork, beef],
FISH=[]
}
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishedByTypeCaloricLevel = enu.stream().collect(
groupingBy(Dish::getType,
groupingBy((Dish dish) -> {
if (dish.getCalories() <= 400) {
return CaloricLevel.DIET;
}
else if (dish.getCalories() <= 700) {
return CaloricLevel.NORMAL;
}
else {
return CaloricLevel.FAT;
}
})
)
);
{
MEAT={
DIET=[chicken],
NORMAL=[beef],
FAT=[pork]
},
FISH={
DIET=[prawns],
NORMAL=[salmon]
},
OTHER={
DIET=[rice, seasonal fruit],
NORMAL=[french fries, pizza]
}
}
Map<Dish.Type, Long> typesCount = menu.stream().collect(
groupingBy(Dish::getType, counting()));
{
MEAT=3,
FISH=2,
OTHER=4
}
요리의 종류를 분류하는 컬렉터로 메뉴에서 가장 높은 칼로리를 가진 요리를 찾는 프로그램을 구현할 수 있다.
Map<Dish.Type, Optional<Dish>> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
maxBy(comparingInt(Dish::getCalories))));
{
FISH=Optional[salmon],
OTHER=Optional[pizza],
MEAT=Optional[pork]
}
Map<Dish.Type, Dish> mostCaloricByType =
menu.stream().collect(
groupingBy(Dish::getType, // 분류 함수
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)), // 감싸인 컬렉터
Optional::get))); // 변환 함수
팩토리 메서드 collectingAndThen은 적용할 컬렉터와 변환 함수를 인수로 받아 다른 컬렉터를 반환한다.