6장에서 배운 것
- Collectors 클래스로 컬렉션 만들고 사용하기
- 스트림 리듀스
- 특별한 리듀싱 요약 연산
- 데이터 그룹화와 분할
- 커스텀 컬렉터
🍎Collector
- 스트림 전체는 reduce(), 그룹별로 나눠 사용할 때는 collect() 메서드를 사용한다.
- collect메서드의 파라미터는 Collector 인터페이스이다.
- Collector 인터페이스의 구현 클래스 Collectors 클래스가 있다.
🍎 Collectors의 메서드 정리
요약 연산 - summingInt, averagingInt, summarizingInt
- summingInt, summingDouble 등 : 값을 누적하여 더한다.
- averagingInt, averagingDoublre 등 : 평균값 계산
- summarizingInt, summarizingDouble 등 : 합계, 평균, 최댓값, 최솟값 등을 한 번에 계산
import static java.util.stream.Collectors.*;
//summingInt 예시
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
//averagingInt 예시
double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories));
//summarizingInt 예시
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
//IntSummaryStatistics{count, sum, min, average, max} 값이 담겨있다.
최대값, 최솟값 구하기 - maxBy, minBy
// 점수 높은 학생 뽑기
OptionalInt topScore = studentStream.mapToInt(Student::getTotalScore).max();//Stream<Student> -> IntStream ,max 점수뽑기
Optional<Student> topStudent = stuStream1.max(Comparator.comparingInt(Student::getTotalScore)); // 점수 높은 학생 뽑기
Optional<Student> top = stuStream2.collect(Collectors.maxBy(Comparator.comparingInt(Student::getTotalScore)));//위와같음
문자열 연결 - joining
- 스트림의 모든 요소(문자열인 경우) 하나의 문자열로 연결한다.
- joining 메서드는 내부적으로 StringBuilder를 이용해서 문자열을 하나로 만듦.
import static java.util.stream.Collectors.*;
String stuNames = stuStream.map(Student::getName).collect(joining());
String stuNames = stuStream.map(Student::getName).collect(joining(","));
String stuNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));
리듀싱 - reducing
- 초기값에 BinaryOperator를 계산하여 누적함. reduce랑 동작방식 같음
- reducing 세 가지
reducing(초기값, Function, BinaryOperator)
- Function을 계산한 결과로 BinaryOperator를 실행.
reducing(초기값,BinaryOperator)
reducing(BinaryOperator)
- reduce 메서드와 마찬가지로 초기값이 없으면 Optional로 반환한다.
// 메뉴의 모든 칼로리 합계 계산하기
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i,j) -> i + j));
// 칼로리 높은 요리 찾기
Optional<Dish> mostCalorieDish = menu.stream().collect(reducing((d1, d2) -> d1.getCalories > d2.getCalories() ? d1 : d2));
- collect와 reduce 차이
- collect는 누적 계산 후 그 결과를 변환해도 되지만, reduce는 누적하여 계산한 결과를 변환하면 의미적으로 잘 못 사용되는 것이다.
- 병렬 : reduce를 병렬 떄 사용하면 누적값에 대해 동시성 문제가 발생한다. 따라서 병렬 실행 때는 collect를 사용해야 한다.
그룹화 - groupingBy
- 종류 세 가지
groupingBy(Function)
- 사실
groupingBy(Function, toList()) 이다.
groupingBy(Function, Collector)
- Function으로 그룹화한 뒤, Collector 실행
groupingBy(Function, Supplier)
- 요리 종류로 단순 그룹화하기
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType));
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(groupingBy( dish -> {
if(dish.getCalories() <= 400) {
return CaloricLevel.DIET;
} else if (dish.getCalories() <= 700) {
return CaloricLevel.NORMAL;
} else {
return CaloricLevel.FAT;
}
}));
// 칼로리 높은 음식 그룹화하기
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList())));
// 이름으로 그룹화하기
Map<Dish.Type, List<String>> dishNamesByType = menu.stream().collect(groupingBy(Dish::getType), mapping(Dish::getName, toList()));
- 다중화 그룹화
- gourpingBy를 두 번 써서 여러 번 그룹화가 가능하다.
중첩 컬렉터 - collectingAndThen
- collectingAndThen(Collector, Function)
- Collector 메서드를 실행한 결과로 Function을 실행한다.
// 타입으로 그룹화하고, 요리 중 칼로리 높은 것울 추출하고, 그 결과를 Optional::get을 실행하여 반환한다.
menu.stream().collect(groupingBy(Dish::getType, collectingAndThen(maxBy(comparingInt(Dish::getCalories)), Optional::get)));
ture, false로 그룹화 - partitioningBy
- partitioningBy(Predicate)
- partitioningBy(Predictae, Collector)
// 채식인 요리, 아닌 요리로 그룹화하기
Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));
🍎Collector 인터페이스의 추상 메서드
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
}
- T는 수집될 스트림의 타입이다.
- A는 누적자, 즉 중간 결과를 누적하는 객체의 타입이다.
- R은 연산 결과 타입이다.
댓글