[Java] 23. 스트림 최종연산(통계와 그룹화 및 분할)


스트림의 최종연산에 대한 포스팅과 스트림 최종연산 중 하나인 collect() 메서드Collector 인터페이스를 통한 통계그룹화(Grouping)분할(partitioning) 연산에 대한 포스팅이다.


1. 스트림의 최종연산

최종 연산자는 스트림 파이프라인의 끝을 나타내며, 스트림의 요소를 소모하여 실제 결과를 반환합니다. 이 연산자가 호출되기 전까지 중간 연산은 실제로 수행되지 않습니다. 최종 연산자는 스트림을 닫는 역할을 한다.

최종 연산설명
void forEach(Consumer<? super T> action)
void forEachOrdered(Consumer<? super T> action)
각 요소에 지정된 작업 수행
long count()스트림의 요소의 개수 반환
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)
스트림의 최대값/최소값 반환
Optional<T> findAny()
Optional<T> findFirst()
스트림의 요소 아무거나 하나를 반환
스트림의 첫번째 요소 하나를 반환
boolean allMatch(Predicate<T> p)
boolean anyMatch(Predicate<T> p)
boolean noneMatch(Predicate<T> p)
모든 요소를 만족하는지 확인
하나라도 요소를 만족하는지 확인
모든 요소를 만족시키지 않는지 확인
Object[] toArray()
A[] toArray(IntFunction<A[]> generator)
스트림의 모든 요소를 배열로 반환
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(<T> identity, BinaryOperator<T> accumulator)
U reduce(<U> identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner)
스트림의 요소를 하나씩 줄여가면서(리듀싱) 계산
R collect(Collector<T,A,R> collector)
R collect(Supplier<R> supplier, BiConsumer<R,T> accumulator, BiConsumer<R.R> combiner)
주로 요소를 그룹화하거나 분할한 결과를 컬렉션으로 반환



◼︎ forEach()

stream.forEach()은 중첩된 구조의 요소들을 평면화(flatten)하는 데 사용된다.

//직렬로 모든 요소에 대해 지정 된 작업을 수행
void forEach(Consumer<? super T> action)   
//모든 요소에 대해 순서를 보장 된 작업을 수행, 병렬 스트림 경우 순서가 보장
void forEachOrdered(Consumer<? super T> action)
//기본 sequential() 직렬 스트림
IntStream.range(1, 10).sequential().forEach(System.out::print);
IntStream.range(1, 10).sequential().forEachOrdered(System.out::print);

//parallel() 병렬 스트림
//- forEach : 병렬 스트림 경우 순서가 보장되지 않음
IntStream.range(1, 10).parallel().forEach(System.out::print);
//- forEachOrdered : 병렬 스트림 경우 순서가 보장됨
IntStream.range(1, 10).parallel().forEachOrdered(System.out::print);



◼︎ count()

stream.count() 메서드는 해당 스트림에서 요소의 개수를 세는 데 사용된다.

//스트림의 전체 카운팅
long totCnt1 = accSubItems.stream().count();
long totCnt2 = accSubItems.stream().collect(Collectors.counting());



◼︎ min(), max()

stream.min()/max()는 스트림의 요소 중 가장 작은 값, 큰 값을 반환한다.

- Optional<T> max(Comparator<? super T> comparator)
- Optional<T> min(Comparator<? super T> comparator)
Optional<Integer> max = numbers.stream().max(Integer::compareTo);



◼︎ match()

stream에 조건 검사 allMath(), anyMatch(), noneMatch() 와 findFirst(), findAny() 최종연산에 대한 포스팅이다.

boolean allMatch(Predicate<? super T> predicate)      //모든 요소를 만족하는지 확인
boolean anyMatch(Predicate<? super T> predicate)      //하나라도 요소를 만족하는지 확인
boolean noneMatch(Predicate<? super T> predicate)     //모든 요소를 만족시키지 않는지 확인
boolean allPositive = numbers.stream().allMatch(num -> num > 0);
System.out.println("모든 요소가 양수인가요? " + allPositive);

boolean anyNegative = numbers.stream().anyMatch(num -> num < 0);
System.out.println("하나라도 음수인 요소가 있나요? " + anyNegative);

boolean noneZero = numbers.stream().noneMatch(num -> num == 0);
System.out.println("모든 요소가 0이 아닌가요? " + noneZero);



◼︎ find()

두 메서드 모두 Optional을 반환하는데, 이는 스트림에서 요소를 찾지 못했을 경우에 대해서이다. findFirst()findAny()는 요소를 찾으면 해당 요소를 담은 Optional을 반환하고, 요소가 없는 경우에는 Optional.empty()를 반환한다.

Optional<T> findFirst()         //첫번째 요소를 반환, 순차 스트림에 사용
Optional<T> findAny()           //아무 요소 하나를 반환, 병렬 스트림에 사용
Optional<AccSubItem> retFirst = AccSubItems.stream()
                                        .filter(e -> e.getAccId().startsWith("B"))
                                        .findFirst();
System.out.println("# findFirst = " + retFirst.get());   //순차적으로 filter 조건에 맞는 "B222"만 반환

Optional<AccSubItem> retAny = AccSubItems.parallelStream()
                                        .filter(e -> e.getAccId().startsWith("B"))
                                        .findAny();
System.out.println("# retAny = " + retAny.get());        //병렬처리시에 filter 조건에 맞는 아무 쓰레드에서 반환



◼︎ reduce()

stream.reduce()는 스트림의 모든 요소를 하나로 줄이는 연산을 수행합니다.

Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner)
  • identity : 초기값
  • accumulator : 이전 연산결과와 스트림의 요소에 수행할 연산
  • combiner : 병렬처리된 결과를 합치는데 사용할 연산(병렬 스트림)
  • [Java] Stream Reduce 최종연산
//T reduce(T identity, BinaryOperator<T> accumulator)
int count = numbers.stream().reduce(0, (a, b) -> a+1);
System.out.println("# count : " + count);

int sum = numbers.stream().reduce(0, (a, b) -> a+b);
System.out.println("# sum : " + sum);

int min = numbers.stream().reduce(Integer.MAX_VALUE, (a, b) -> a<b ? a:b);
System.out.println("# min : " + min);

int max = numbers.stream().reduce(Integer.MIN_VALUE, (a, b) -> a>b ? a:b);
System.out.println("# max : " + max);

//Optional<T> reduce(BinaryOperator<T> accumulator)
Optional<Integer> optMin = numbers.stream().reduce(Integer::min);
System.out.println("# optMin : " + optMin.get());

Optional<Integer> optMax = numbers.stream().reduce(Integer::max);
System.out.println("# optMax : " + optMax.get());



1-1. 스트림의 컬렉션 변환

◼︎ collect()

Collector 인터페이스는 스트림의 collect() 메서드와 함께 사용되며, 스트림의 요소를 여러 그룹으로 분할하거나, 요소들을 리스트, 세트, 맵 등의 컬렉션으로 수집하고, 통계 수치를 계산하는 등의 기능을 수행합니다.


◼︎ toList()

// Collectors.toList()를 사용하여 List로 수집
List<AccSubItem2> collectedToList = accSubItems.stream()
        .collect(Collectors.toList());


◼︎ toSet()

// Collectors.toSet()를 사용하여 Set으로 수집
Set<AccSubItem2> collectedToSet = accSubItems.stream()
        .collect(Collectors.toSet());


◼︎ toMap()

// Collectors.toMap()를 사용하여 Map으로 수집 (AccId 를 key로 사용)
// Key duplication 오류로 toMap() 사용 시 mergeFunction 지정
// -> (existing, replacement) -> existing 부분은 중복 키가 발생했을 때 기존 값(existing)을 유지하는 방법
Map<String, AccSubItem2> collectedToMap = accSubItems.stream()
        .collect(Collectors.toMap(AccSubItem2::getAccId, item -> item, (existing, replacement) -> existing));


◼︎ toArray()

// toArray 를 활용해 List 를 Array 형태로 변환
AccSubItem2[] collectedToArray = accSubItems.stream().toArray(AccSubItem2[]::new);



1-2. 스트림의 통계


◼︎ counting() : 전체 건수 구하기

//스트림의 전체 카운팅
long totCnt1 = accSubItems.stream().count();
long totCnt2 = accSubItems.stream().collect(Collectors.counting());
System.out.println("# 1 .totCnt1 = " + totCnt1 + ",  totCnt2 = " + totCnt2);
# 1 .totCnt1 = 10,  totCnt2 = 10


◼︎ summingInt() : 합계 구하기

//int 형 전체 합계 점수
int sumScore1 = accSubItems.stream().mapToInt(AccSubItem2::getScore).sum();
int sumScore2 = accSubItems.stream().collect(Collectors.summingInt(AccSubItem2::getScore));
System.out.println("# 2. sumScore1 = " + sumScore1 + ",  sumScore2 = " + sumScore2);
# 2. sumScore1 = 5950,  sumScore2 = 5950


◼︎ reducing() : reduce 로 BigDecimal 합계 구하기

//BigDecimal 형 데이터 전체 합계 금액은 reduce() 로 구하기 예제
BigDecimal sumAmt1 = accSubItems.stream()
                                .map(AccSubItem2::getAccAmt)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal sumAmt2 = accSubItems.stream()
                                .map(AccSubItem2::getAccAmt)
                                .collect(Collectors.reducing(BigDecimal.ZERO, BigDecimal::add));
System.out.println("# 3. sumAmt1 = " + sumAmt1);
System.out.println("#    sumAmt2 = " + sumAmt2);
# 3. sumAmt1 = 945346
#    sumAmt2 = 945346


◼︎ min(), max() : 최소, 최대 값 구하기

//mapToInt(Integer) , mapToLong(Long) 타입 min, max 구하기
OptionalInt minScore = accSubItems.stream()
                                .mapToInt(AccSubItem2::getScore)
                                .min();
OptionalInt maxScore = accSubItems.stream()
                                .mapToInt(AccSubItem2::getScore)
                                .max();
System.out.println("# 4. minScore = " + minScore.getAsInt() + ",  maxScore = " + maxScore.getAsInt());
# 4. minScore = 120,  maxScore = 900


◼︎ min(), max() 에 Comparator.comparingInt() 활용

//AccSubItem 객체 중에 score 점수기준으로 min, max 객체 구하기
Optional<AccSubItem2> minAccSubItem = accSubItems.stream()
                                                .min(Comparator.comparingInt(AccSubItem2::getScore));
Optional<AccSubItem2> maxAccSubItem = accSubItems.stream()
                                                .max(Comparator.comparingInt(AccSubItem2::getScore));
System.out.println("# 5. minAccSubItem = " + minAccSubItem);
System.out.println("     maxAccSubItem = " + maxAccSubItem);
# 5. minAccSubItem = Optional[AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=100000, score=120}]
     maxAccSubItem = Optional[AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=1000, score=900}]


◼︎ minBy(), maxBy() 에 Comparator.comparing() 활용

//accAmt 변수 그룹 기준으로 min, max 객체 구하기
Optional<AccSubItem2> minByDepWdrTypCd = accSubItems.stream()
                                                    .collect(Collectors.minBy(Comparator.comparing(AccSubItem2::getAccAmt)));
Optional<AccSubItem2> maxByDepWdrTypCd = accSubItems.stream()
                                                    .collect(Collectors.maxBy(Comparator.comparing(AccSubItem2::getAccAmt)));
System.out.println("# 6. minByDepWdrTypCd = " + minByDepWdrTypCd);
System.out.println("     maxByDepWdrTypCd = " + maxByDepWdrTypCd);
# 6. minByDepWdrTypCd = Optional[AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=1000, score=900}]
     maxByDepWdrTypCd = Optional[AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}]


◼︎ averagingInt() : 평균 구하기

// 7. score를 기준으로 평균 계산하여 출력
Double averageScore = accSubItems.stream()
                                .collect(Collectors.averagingInt(AccSubItem2::getScore));
System.out.println("# 7. Score 의 평균 = " + averageScore);
# 7. Score 의 평균 = 595.0



1-3. 스트림 문자열 결합


◼︎ joining()

String joiningStr = accSubItems.stream().map(AccSubItem2::getAccId).collect(Collectors.joining());
System.out.println("# joiningStr1 = " + joiningStr);

joiningStr = accSubItems.stream().map(AccSubItem2::getAccId).collect(Collectors.joining("|"));
System.out.println("# joiningStr2 = " + joiningStr);

joiningStr =accSubItems.stream().map(AccSubItem2::getAccId).collect(Collectors.joining(",", "{", "}"));
System.out.println("# joiningStr3 = " + joiningStr);
# joiningStr1 = A123A123A123A201A222A510A510A510A610A105
# joiningStr2 = A123|A123|A123|A201|A222|A510|A510|A510|A610|A105
# joiningStr3 = {A123,A123,A123,A201,A222,A510,A510,A510,A610,A105}



1-4. 스트림 GroupingBy 그룹 연산

Collectors.groupingBy() 는 스트림 요소를 지정된 기준에 따라 그룹화하는 기능으로 이를 통해 데이터를 그룹별로 분류하고 집계할 수 있다.

Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)


◼︎ groupingBy 기본 예제

// DepWdrTypCd 멤버변수 기준으로 groupBy 별 건수
Map<String, Long> accSubGroupByCnt = accSubItems.stream()
        .collect(Collectors.groupingBy(AccSubItem2::getDepWdrTypCd, Collectors.counting()));
System.out.println("DepWdrTypCd 멤버변수 기준으로 groupBy 별 건수: " + accSubGroupByCnt);

// DepWdrTypCd 멤버변수 기준으로 groupBy 별로 객체 List
Map<String, List<AccSubItem2>> accSubGroupByMap = accSubItems.stream()
                                                        .collect(Collectors.groupingBy(AccSubItem2::getDepWdrTypCd));
accSubGroupByMap.get("10").forEach(accSubItem -> {
    System.out.println("[10]" + accSubItem.toString());
});
accSubGroupByMap.get("20").forEach(accSubItem -> {
    System.out.println("[20]" + accSubItem.toString());
});
DepWdrTypCd 멤버변수 기준으로 groupBy 별 건수: {20=3, 10=7}
[10]AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=11000, score=700}
[10]AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}
[10]AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}
[10]AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=21000, score=820}
[10]AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=37000, score=790}
[10]AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}
[10]AccSubItem2{accId='A105', depWdrTypCd='10', accAmt=12346, score=880}
[20]AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}
[20]AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}
[20]AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}


◼︎ groupingBy n분할 일반적인 그룹화

그룹화 된 그룹명이 정렬 되지 않은 상태이다.

// n분할 그룹화
Map<String, List<AccSubItem2>> gradeGroupBy = accSubItems.stream()
        .collect(Collectors.groupingBy(
                accSubItem -> {
                    if (accSubItem.getScore() >= 900) return "A 등급";
                    else if (accSubItem.getScore() >= 800) return "B 등급";
                    else if (accSubItem.getScore() >= 700) return "C 등급";
                    else return "D 등급";
                }
        ));

System.out.println("========== 일반적인 n분할 그룹화 ==========");
gradeGroupBy.forEach((key, accSubItem) -> {
    System.out.println("[" + key + "]");
    accSubItem.forEach(System.out::println);
});
========== 일반적인 n분할 그룹화 ==========
[C 등급]
AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=11000, score=700}
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=37000, score=790}
[D 등급]
AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}
AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}
AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}
AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}
[B 등급]
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=21000, score=820}
AccSubItem2{accId='A105', depWdrTypCd='10', accAmt=12346, score=880}
[A 등급]
AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}


◼︎ groupingBy n분할 정렬/역정렬 그룹화

TreeMap 을 활용한 정렬/역정렬 grouping 예제이다.

TreeMap<String, List<AccSubItem2>> orderGradeGroupBy = accSubItems.stream()
        .collect(Collectors.groupingBy(
                accSubItem -> {
                    if (accSubItem.getScore() >= 900) return "A 등급";
                    else if (accSubItem.getScore() >= 800) return "B 등급";
                    else if (accSubItem.getScore() >= 700) return "C 등급";
                    else return "D 등급";
                }
                , TreeMap::new  //A등급, B등급, C등급, D등급
                , Collectors.toList()
            )
        );

System.out.println("========== 정렬 n분할 그룹화 ==========");
orderGradeGroupBy.forEach((key, accSubItem) -> {
    System.out.println("[정렬][" + key + "]");
    accSubItem.forEach(System.out::println);
});

System.out.println("========== 정렬 역순 n분할 그룹화 ==========");
orderGradeGroupBy.descendingMap().forEach((key, accSubItem) -> {
    System.out.println("[정렬 역순][" + key + "]");
    accSubItem.forEach(System.out::println);
});
========== 정렬 n분할 그룹화 ==========
[정렬][A 등급]
AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}
[정렬][B 등급]
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=21000, score=820}
AccSubItem2{accId='A105', depWdrTypCd='10', accAmt=12346, score=880}
[정렬][C 등급]
AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=11000, score=700}
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=37000, score=790}
[정렬][D 등급]
AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}
AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}
AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}
AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}
========== 정렬 역순 n분할 그룹화 ==========
[정렬 역순][D 등급]
AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}
AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}
AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}
AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}
[정렬 역순][C 등급]
AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=11000, score=700}
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=37000, score=790}
[정렬 역순][B 등급]
AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=21000, score=820}
AccSubItem2{accId='A105', depWdrTypCd='10', accAmt=12346, score=880}
[정렬 역순][A 등급]
AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}


◼︎ groupingBy n분할 통계

TreeMap<String, Long> orderGradeGroupByCnt = accSubItems.stream()
        .collect(Collectors.groupingBy(
                accSubItem -> {
                    if (accSubItem.getScore() >= 900) return "A 등급";
                    else if (accSubItem.getScore() >= 800) return "B 등급";
                    else if (accSubItem.getScore() >= 700) return "C 등급";
                    else return "D 등급";
                }
                , TreeMap::new  //A등급, B등급, C등급, D등급
                , Collectors.counting()
            )
        );
System.out.println("========== 정렬 n분할 그룹화 통계 ==========");
orderGradeGroupByCnt.forEach((key, groupCnt) -> {
    System.out.println("[" + key + "] cnt : "  + groupCnt);
});
========== 정렬 n분할 그룹화 통계 ==========
[A 등급] cnt : 1
[B 등급] cnt : 2
[C 등급] cnt : 2
[D 등급] cnt : 5


◼︎ groupingBy 다중 분할(DepWdrTypCd, Score)

TreeMap<String, Map<String, List<AccSubItem2>>> multiGroupBy = accSubItems.stream()
        .collect(Collectors.groupingBy(
                AccSubItem2::getDepWdrTypCd, TreeMap::new, Collectors.groupingBy(
                        accSubItem -> {
                            if (accSubItem.getScore() >= 900) return "A 등급";
                            else if (accSubItem.getScore() >= 800) return "B 등급";
                            else if (accSubItem.getScore() >= 700) return "C 등급";
                            else return "D 등급";
                        }, TreeMap::new, Collectors.toList()
                )
        ));

System.out.println("========== 다중 분할(DepWdrTypCd, Score) 그룹화 ==========");
multiGroupBy.forEach((depWdrKey, scoreGroup) -> {
    System.out.println("# DepWdrTypCd: " + depWdrKey);
    scoreGroup.forEach((scoreKey, items) -> {
        System.out.println("\tScore: " + scoreKey);
        items.forEach(e-> System.out.println("        - " + e));
    });
});
========== 다중 분할(DepWdrTypCd, Score) 그룹화 ==========
# DepWdrTypCd: 10
	Score: B 등급
        - AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=21000, score=820}
        - AccSubItem2{accId='A105', depWdrTypCd='10', accAmt=12346, score=880}
	Score: C 등급
        - AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=11000, score=700}
        - AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=37000, score=790}
	Score: D 등급
        - AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}
        - AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}
        - AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}
# DepWdrTypCd: 20
	Score: A 등급
        - AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}
	Score: D 등급
        - AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}
        - AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}



1-5. 스트림 PartitioningBy 분할 연산

Collectors.partitioningBy()은 요소들을 참(true)인 그룹과 거짓(false)인 boolean 그룹으로 분할해서 두 개의 그룹을 맵(Map)으로 반환한다. partitioningBy 는 2분할로 Key가 boolean 으로 true, false 이다.

Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downStream)


◼︎ partitioningBy 분할

//DepWdrTypCd "10"과 "20"으로 partitioningBy 분할
Map<Boolean, List<AccSubItem2>> partitioningBy = accSubItems.stream()
        .collect(Collectors.partitioningBy(item -> "10".equals(item.getDepWdrTypCd())));
System.out.println("# partitioningBy");
System.out.println("- DepWdrTypCd(10) : " + partitioningBy.get(true));
System.out.println("- DepWdrTypCd(20) : " + partitioningBy.get(false));
# partitioningBy
- DepWdrTypCd(10) : [AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=11000, score=700}, AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}, AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}, AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=21000, score=820}, AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=37000, score=790}, AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}, AccSubItem2{accId='A105', depWdrTypCd='10', accAmt=12346, score=880}]
- DepWdrTypCd(20) : [AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}, AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}, AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}]


◼︎ partitioningBy 통계

//DepWdrTypCd "10"과 "20"으로 partitioningBy 통계
Map<Boolean, Long> partitioningByCnt = accSubItems.stream()
        .collect(Collectors.partitioningBy(item -> "10".equals(item.getDepWdrTypCd()), Collectors.counting()));
System.out.println("# partitioningByCnt");
System.out.println("- DepWdrTypCd(10) Cnt : " + partitioningByCnt.get(true));
System.out.println("- DepWdrTypCd(20) Cnt : " + partitioningByCnt.get(false));
# partitioningByCnt
- DepWdrTypCd(10) Cnt : 7
- DepWdrTypCd(20) Cnt : 3


◼︎ partitioningBy 분할 + 통계(min)

//DepWdrTypCd "10"과 "20"으로 partitioningBy 통계 Integer 형 Min Max
Map<Boolean, Optional<AccSubItem2>> partitioningByMin = accSubItems.stream()
        .collect(Collectors.partitioningBy(
                item -> "10".equals(item.getDepWdrTypCd())
                , Collectors.minBy(Comparator.comparingInt(AccSubItem2::getScore))
        ));
System.out.println("# partitioningBy Integer Min");
System.out.println("- DepWdrTypCd(10) By Score Min : " + partitioningByMin.get(true));
System.out.println("- DepWdrTypCd(20) By Score Min : " + partitioningByMin.get(false));
# partitioningBy Integer Min
- DepWdrTypCd(10) By Score Min : Optional[AccSubItem2{accId='A123', depWdrTypCd='10', accAmt=4000, score=120}]
- DepWdrTypCd(20) By Score Min : Optional[AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}]


◼︎ partitioningBy 분할 + 통계(max)

//DepWdrTypCd "10"과 "20"으로 partitioningBy 통계 BigDecimal 형 Min Max
Map<Boolean, Optional<AccSubItem2>> partitioningByMax = accSubItems.stream()
        .collect(Collectors.partitioningBy(
                item -> "10".equals(item.getDepWdrTypCd()),
                Collectors.maxBy(Comparator.comparing(AccSubItem2::getAccAmt))
        ));
System.out.println("# partitioningBy BigDecimal Max");
System.out.println("- DepWdrTypCd(10) By AccAmt Max : " + partitioningByMax.get(true));
System.out.println("- DepWdrTypCd(20) By AccAmt Max : " + partitioningByMax.get(false));
# partitioningBy BigDecimal Max
- DepWdrTypCd(10) By AccAmt Max : Optional[AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}]
- DepWdrTypCd(20) By AccAmt Max : Optional[AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}]


◼︎ partitioningBy 다중 분할

//DepWdrTypCd "10"과 "20" 와 AccAmt 100000 이상 partitioningBy 다중 분할
Map<Boolean, Map<Boolean, List<AccSubItem2>>> partitionInPartitioningBy = accSubItems.stream()
        .collect(
                Collectors.partitioningBy(item -> "10".equals(item.getDepWdrTypCd())
                , Collectors.partitioningBy(item -> item.getAccAmt().compareTo(new BigDecimal(100000)) >= 0)
        ));
System.out.println("# partition In PartitioningBy ");
System.out.println("- DepWdrTypCd(10) By 100000 이상 By : " + partitionInPartitioningBy.get(true).get(true));
System.out.println("- DepWdrTypCd(20) By 100000 이상 By : " + partitionInPartitioningBy.get(false).get(true));
# partition In PartitioningBy 
- DepWdrTypCd(10) By 100000 이상 By : [AccSubItem2{accId='A201', depWdrTypCd='10', accAmt=128000, score=320}, AccSubItem2{accId='A510', depWdrTypCd='10', accAmt=432000, score=240}]
- DepWdrTypCd(20) By 100000 이상 By : [AccSubItem2{accId='A123', depWdrTypCd='20', accAmt=103000, score=670}, AccSubItem2{accId='A222', depWdrTypCd='20', accAmt=100000, score=900}, AccSubItem2{accId='A610', depWdrTypCd='20', accAmt=431000, score=510}]




[출처]

  • 자바의정석 (저자: 남궁성)