
저자: 라울-게이브리얼 우르마 , 마리오 푸스코 , 앨런 마이크로프트
도서명: 모던 자바 인 액션
출판사: 한빛미디어
Collect : Collector를 매개변수로 하는 스트림의 최종 연산
Collector : Collect에서 필요한 메서드를 정의해놓은 인터페이스
Collectors : 다양한 기능의 Collector를 구현한 클래스 제공
Collect는 최종연산이며 스트림의 요소를 소비해서 최종 결과를 도출한다.


import static java.util.stream.Collectors.*;
long howManyDishes = menu.stream().collect(counting());
long howManyDishes = menu.stream().count();
Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCaloriesDish = menu.stream().collect(maxBy(dishCaloriesComparator));
Optional<Dish> mostCaloriesDish = menu.stream().collect(minBy(dishCaloriesComparator));
합계 : summingInt, summingDouble, summingLong
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
평균 : averagingInt,averaging, averagingDouble
double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories));
다중값 ( 카운터, 합계, 최소값, 평균, 최대값 ) : summarizingInt, summarizingLong, summarizingDouble
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
String shortMenu = menu.stream().map(Dish::getName).collect(joing(","));
int totalCalories = menu.stream().collect( reducing( 0, Dish::getCalories,(i,j)->i+j ));
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]}
// With a classification function as the method parameter:
// T를 K로 매핑하고, K키에 저장된 List에 T를 저장한 Map 생성
static <T,K> Collector<T,?,Map<K,List<T>>>
groupingBy(Function<? super T,? extends K> classifier)
// With a classification function and a second collector as method parameters:
// T를 K로 매핑하고, K키에 저장된 D객체에 T를 누적한 Map 생성
static <T,K,A,D> Collector<T,?,Map<K,D>>
groupingBy(Function<? super T,? extends K> classifier,
Collector<? super T,A,D> downstream)
// With a classification function, a supplier method (that provides the Map implementation that will contain the end result), and a second collector as method parameters:
// T를 K로 매핑하고 Supplier가 제공하는 Map에서 K키에 저장된 D객체에 T를 누적
static <T,K,D,A,M extends Map<K,D>> Collector<T,?,M>
groupingBy(Function<? super T,? extends K> classifier,
Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
참고 :
https://recordsoflife.tistory.com/55 , https://yongho1037.tistory.com/704
Collector 인터페이스는 리듀싱 연산을 어떻게 구현할지 제공하는 메서드 집합으로 구성된다.
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 : 수집 연산 결과 객체의 형식 [ 대다수가 컬렌션 ]
supplier : 새로운 결과 컨테이너 만들기
public Supplier<List<T>> supplier() {
return () -> new ArrayList<T>;
}
accumlator : 결과 컨테이너에 요소 추가하기
리듀싱 연산을 수행하는 함수를 반환한다. 각 요소를 처리하는 계산 로직. 각 요소가 올 때마다 중간 결과를 생성하는 로직
public BiConsumer(List<T>, T> accumulator() {
return (list, time) -> list.add(item);
}
finisher : 최종 변환값을 결과 컨테이너로 적용
스트림 탐색을 끝내고 누적자 객체를 최종 결과로 반환하면서 누적 과정을 끝낼 때 호출할 함수를 반환해야한다.
public Function<List<T>, List<T>> finisher() {
return Function.identity();
}
combiner : 두 결과 컨테이너 병합
스트림의 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이 결과를 어떻게 처리할 지 정의한다. 즉, combiner의 역할을 identity[초기값]와 accumulator[중간 로직]를 가지고 여러스레드에서 나눠 계산할 결과를 합치는 역할
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
}
}
charateristics
컬렉터의 연산을 정의하는 Charateristices 형식의 불변 집합을 반환한다.
public Set<Characteristics> charateristics() {
return Collections.unmodifiableSet(EnumSet.of(
IDENTITY_FINISH, CONCURRENT));
}
참고 : https://catsbi.oopy.io/89530e57-cafd-4178-b989-25e1dc45cfdb
https://velog.io/@ggomjae/Collect-Collector-Collectors-OO
https://hongilhwang.gitbooks.io/java8inaction/content/chapter6.html
https://cornswrold.tistory.com/387
https://catsbi.oopy.io/89530e57-cafd-4178-b989-25e1dc45cfdb