[JAVA] 스트림으로 데이터 수집

Jae-Baek Song·2023년 1월 31일

모던자바인액션

목록 보기
5/11
post-thumbnail

저자: 라울-게이브리얼 우르마 , 마리오 푸스코 , 앨런 마이크로프트
도서명: 모던 자바 인 액션
출판사: 한빛미디어


Collect : Collector를 매개변수로 하는 스트림의 최종 연산
Collector : Collect에서 필요한 메서드를 정의해놓은 인터페이스
Collectors : 다양한 기능의 Collector를 구현한 클래스 제공

Collect는 최종연산이며 스트림의 요소를 소비해서 최종 결과를 도출한다.

리듀싱과 요약

counting()

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 인터페이스는 리듀싱 연산을 어떻게 구현할지 제공하는 메서드 집합으로 구성된다.

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));
}

Collector 인터페이스의 각 메소드 기능

  • supplier 메소드
    . 작업 결과를 저장할 공간을 제공
  • accumulator 메소드
    . 스트림의 요소를 수집할 방법을 제공
    . 스트림의 요소들을 supplier 메소드가 제공한 공간에 누적할 방법에 대해 정의
  • combiner 메소드
    . 두 저장공간을 병합할 방법을 제공 (병렬 스트림)
    . 여러 스레드에 의해 처리된 결과를 어떻게 합칠 것인지에 대해 정의
  • finisher 메소드
    . 결과를 최종적으로 변환할 방법을 제공
    . 변환이 필요 없다면 Function 함수형 인터페이스의 identity 함수를 반환도록 구현
  • characteristics 메소드
    . 컬렉터가 수행하는 작업의 속성에 대한 정보를 제공
    . 아래 속성 중 해당하는 것을 Set 컬렉션 프레임워크에 담아서 반환
    • Characteristics.CONCURRENT : 병렬로 처리할 수 있는 작업
    • Characteristics.UNORDERED : 스트림 요소의 순서가 유지될 필요가 없는 작업
    • Characteristics.IDENTITY_FINISH : finisher 메소드가 항등 함수(Function.identity())인 작업

참고 : 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

https://thalals.tistory.com/361

0개의 댓글