본문 바로가기
Modern java in action

[modern java in action] stream(5)

by 상국이 2021. 6. 24.
728x90

5-1 컬렉터린 무엇인가?

→ Collector 인터페이스 구현은 스트림의 요소를 어떤 식으로 도출할지 지정한다. / collect로 결과를 수집하는 과정을 간단하고 유연한 방식으로 정의할 수 있다는 점이 장점

보통 함수를 요소로 변환할 때는 컬렉터를 적용하며 최종결과를 저장하는 자료구조에 값을 누적한다.

  미리 정의된 컬렉터

  • 스트림 요소를 하나의 값으로 리듀스하고 요약
  • 요소 그룹화
  • 요소 분할

 

5-2 리듀싱과 요약

→ 컬렉터로 스트림의 모든 항목을 하나의 결과로 합칠 수 있다. 트리를 구성하는 다수준 맵, 메누의 말로리 합계를 가리키는 단순한 정수 등 다양한 형식으로 결과가 도출될 수 있다.

long howManyDishes = menu.stream().collect(Collectors.counting());
 
long howManyDishes = menu.stream().count();     //java.util.stream.Collectors.*;를 임포트했다고 가정

  (1) 요약 연산

  → Collector 클래스는  Collector.summingInt라는 요약 펙토리 메서드를 제공한다. 이는 객체를 int로 매핑하는 함수를 인수로 받고, 객체를 int로 매핑한 컬텍터를 반환한다.

int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));                           //칼로리가 매핑된 각 요리의 값을 탐색하면서 설정되어있는 누적자에 칼로리를 더한다.
 
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));     //IntSummaryStatistics 클래스로 모든 정보가 수집된다.(count, sum, min, max, average)

  (2) 문자열 연결

  → 컬렉터에 joining 팩토리 메서드를 이용하면 스트림의 각 객체에 toString 메서드를 호출해서 추출한 모든 문자열을 하나의 문자열로 연결해서 반환한다.

String shortMenu = menu.stream().collect(joining());                                //구분자 없이 모든 문자열이 연결되어 반환
 
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));         //", "로 문자열 구분

  (3) 범용 리듀싱 요약 연산

  → Collectors.reducing으로 정의 / 범용 팩토리 대신 특화된 컬렉터를 사용하는 이유 : 프로그래밍적 편의성, 가독성 향상

    reducing의 인수

  • 첫 번째 : 리듀싱의 연산의 시작값이나 스트림에 인수가 없을 때는 반환값 
  • 두 번째 : 변환할 때 사용하는 변환함수
  • 세 번째 : 같은 종류의 두 항목을 하나의 값으로 더하는 BinaryOperator
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));

 * collect와 reduce

→ collect 메서드 : 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드 / reduce : 두 값을 하나로 도출하는 불변형 연산 

 * 제네릭 와일드카드  ' ? ' 사용법

→ ?는 누적자 형식이 알려지지 않았음을, 즉 누적자의 형식이 자유로움을 의미한다.

 

5-3 그룹화

분류함수 : 그룹화의 기준이 되는 함수

→ 그룹화 연산의 결과로 그룹화 함수가 반환하는 키 그리고 각 키에 대응하는 스트림의 모든 항목 리스트를 값으로 갖는 맵이 반환된다.

  그룹화된 요소 조작

  팩토리 메서드 Collector.groupingBy 를 이용해서 항목을 그룹화할 수 있다.

Map<Dish.Type, List<Dish>> caloricDishesByType = menu.stream().filter(dish.getCalories()>500)
                                                            .collect(groupingBy(Dish::getType));    //결과가 없으면 키값도 지워짐
 
Map<Dish.Type, List<Dish>> caloricDishesByType = menu.stream()
                                                     .collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList())));      //결과가 없어도 키값이 살아있음

  다수준 그룹화

  두 인수를 받는 팩토리 메서드 Collector.groupingBy 를 이용해서 항목을 다수준으로 그룹화할 수 있다.

Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishsByTypeCaloricLevel =
        menu.stream().collect(
                groupingBy(Dish::getType,
                        groupingBy(dish -> {
                                    if (dish.getCalories() <= 400)                   //Type별로 그룹화
                                        return CaloricLevel.DIET;                   //칼로리별로 그룹화
                                    else if (dish.getCalories() <= 700)
                                        return CaloricLevel.NORMAL;                
                                    else
                                        return CaloricLevel.FAT;
                                })
                )
        );

  서브그룹으로 데이터 수집

  한 개의 인수를 갖는 groupingBy(f)는  groupingBy(f, toList())의 축약형이다.

Map<Dish.Type, Optional<Dish>> mostCaloricByType =
        menu.stream()
            .collect(groupingBy(Dish::getType,
                                maxBy(comparingInt(Dish::getCalories))));           //Optional<Dish>는 해당 종류의 음식 중 가장 높은 칼로리를 래핑한다.
728x90

댓글