[모던 자바 인 액션] Chapter06. 스트림으로 데이터 수집

SunYerim·2024년 2월 13일
0

언어

목록 보기
6/11

6.1 컬렉터란 무엇인가?

명령형 코드에서는 다중 루프와 조건문을 추가하며 가독성과 유지보수성이 크게 떨어진다. 함수형 프로그래밍에서는 필요한 컬렉터를 쉽게 추가할 수 있다.

6.1.1 고급 리듀싱 기능을 수행하는 컬렉터

  • 함수형 API의 장점으로 높은 수준의 조합성과 재사용성을 꼽을 수 있다.
  • collect에서는 리듀싱 연산을 이용해서 스트림의 각 요소를 방문하면서 컬렉터가 작업을 처리한다.
  • 보통 함수를 요소로 변환할 때는 컬렉터를 적용하며 최종 결과를 저장하는 자료구조에 값을 누적한다.
  • Collectors 유틸리티 클래스는 자주 사용하는 컬렉터 인스턴스를 손쉽게 생성할 수 있는 정적 팩토리 메서드를 제공하는데, 가장 많이 사용하는 직관적인 정적 메서드로 toList를 꼽을 수 있다.
    • toList는 스트림의 모든 요소를 리스트로 수집한다.

      List<Transaction> transactions = transactionStream.collect(Collectors.toList());

6.1.2 미리 정의된 컬렉터

Collectors에서 제공하는 메서드의 기능은 크게 세 가지로 구분할 수 있다.

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

6.2 리듀싱과 요약

컬렉터로 스트림의 항목을 컬렉션으로 재구성할 수 있다. 좀 더 쉽게 말하자면 컬렉터로 스트림의 모든 항목을 하나의 결과로 합칠 수 있다.

  • counting()이라는 팩토리 메서드가 반환하는 컬렉터로 메뉴에서 요리 수를 계산한다.
long howManyDishes = menu.stream().collect(Collectors.counting());

// 불필요한 과정 생략
long howManyDishes = menu.stream().count();

6.2.1 스트림값에서 최댓값과 최솟값 검색

예제에서, 메뉴에서 칼로리가 가장 높은 요리를 찾는다고 가정할 때, Collectors.maxBy, Collectors.minBy 두 개의 메서드를 이용해서 스트림의 최댓값과 최솟값을 계산할 수 있다.

두 컬렉터는 스트림의 요소를 비교하는 데 사용할 Comparator를 인수로 받는다.

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);

Optional<Dish> mostCalorieDish = menu.stream()
																		 .collect(maxBy(dishCaloriesComparator));

스트림에 있는 객체의 숫자 필드의 합계나 평균 등을 반환하는 연산에도 리듀싱 기능이 자주 사용된다. ⇒ 요약 연산

6.2.2 요약 연산

Collectors 클래스는 Collectors.summingInt라는 요약 팩토리 메서드를 제공한다.

summingInt는 객체를 int로 매핑하는 함수를 인수로 받는다. 인수로 전달된 함수는 객체를 int로 매핑한 컬렉터를 반환한다.

int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

스트림의 요소 수를 계산하고, 최댓값. 최솟값을 찾고, 합계와 평균을 계산하는 것 들 중 두 개 이상의 연산을 한 번에 수행해야 할 때도 있는데, 이럴때는 팩토리 메서드 summarizingInt가 반환하는 컬렉터를 사용할 수 있다.

IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));

위 코드를 실행하면 모든 정보가 수집된다.

6.2.3 문자열 연결

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

String shortMenu = menu.stream().map(Dish::getName).collect(joining());

joining 메서드는 내부적으로 StringBuilder를 이용해서 문자열을 하나로 만든다.

연결된 두 요소 사이에 구분 문자열을 넣을 수 있도록 오버로드된 joining 팩토리 메서드도 있다.

String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));

6.2.4 범용 리듀싱 요약 연산

모든 컬렉터는 reducing 팩토리 메서드로도 정의할 수 있다.

int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));

reducing은 인수 세 개를 받는다.

  • 첫번째: 리듀싱 연산의 시작값이거나 스트림에 인수가 없을 때는 반환값이다.
  • 두번째: 변환 함수
  • 세번째: 같은 종류의 두 항목을 하나의 값으로 더하는 BinaryOperator다.

추가 설명 들어갈 예정

~2.15


6.3 그룹화

팩토리 메서드 Collectors.groupingBy를 이용해서 메뉴를 그룹화할 수 있다.

Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType));

스트림의 각 요리에서 Dish.Type과 일치하는 모든 요리를 추출하는 함수를 groupingBy 메서드로 전달했다.

⇒ 이 함수를 기준으로 스트림이 그룹화되므로 이를 분류 함수라고도 부른다.

6.3.1 그룹화된 요소 조작

요소를 그룹화한 다음에는 각 결과 그룹의 요소를 조작하는 연산이 필요하다.


6.4 분할

profile
내 안에 있는 힘을 믿어라.

0개의 댓글