스터디 책 정리/모던 자바 인 액션

스트림으로 데이터 수집

박상순 2023. 9. 12. 00:17

Collector

Collector 인터페이스 구현은 스트림 요소를 어떤 식으로 도출할지 지정한다. Collector 인터페이스 메서드를 어떻게 구현하느냐에 따라 스트림에 어떤 리듀싱 연산을 수행할지 결정된다. Collectors 유틸리티 클래스는 자주 사용하는 컬렉터 인스턴스를 손쉽게 생성할 수 있는 정적 팩토리 메서드를 제공한다.

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

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

리듀싱과 요약

  • counting - 개수를 카운트한다
  • maxBy, minBy - 최대 혹은 최소를 만족하는 요소를 찾는다
  • summingInt - 객체를 int로 매핑하는 인수를 받아 합을 계산한다
  • averagingInt - 객체를 int로 매핑하는 인스를 받아 평균을 계산한다
  • summarizingInt - 요소 수, 합계, 평균, 최댓값, 최솟값 등을 계산한다.
  • joining - 내부적으로 StringBuilder를 이용해서 문자열을 하나로 만든다.

collect와 reduce

collect와 reduce를 이용하면 동일한 기능을 구현할 수 있다.

collect 메서드는 도출하려는 결과를 누적하는 컨테이너를 바꾸도록 설계된 메서드인 반면, reduce는 두 값을 하나로 도출하는 불변형 연산이라는 점에서 의미론적인 차이가 존재한다.

여러 스레드가 동시에 같은 데이터 구조체를 고치면 리스트 자체가 망가져버리므로 리듀싱 연산을 병렬로 수행할 수 없다. 이럴 때 가변 컨테이너 관련 작업이면서 병렬성을 확보하려면 collect 메서드로 리듀싱 연산을 구현하는것이 바람직하다.

그룹화

그룹화 함수는 어떤 기준으로 스트림을 분류하는 속성을 가졌기에 분류 함수(classification function)라고 부른다.

  • groupingBy
  • stream().collect(groupingBy(조건)) 형식으로 사용됨
  • 다중으로 사용하여 다중 수준으로 그룹화 가능

그룹핑에 핵심적인 메서드이며 많은 오버로딩된 메서드를 가진다.

분할

분할은 분할 함수(partitioning function)라 불리는 Predicate를 분류 함수로 사용하는 특수한 그룹화 기능이다.

맵의 키 형식은 Boolean이므로 그룹화 맵은 두 개의 그룹으로 분류된다.

분할의 장점은 참, 거짓 두 가지 요소의 스트림 리스트를 모두 유지한다는 것이 장점이다.

  • partitioningBy()

Collector 인터페이스

Collector 인터페이스는 리듀싱 연산을 어떻게 구현할지 제공하는 메서드 집합으로 구성된다. 

  • T는 수집될 항목의 제네릭 형식이다.
  • A는 누적자, 즉 수집 과정에서 중간 결과를 누적하는 객체의 형식이다.
  • R은 수집 연산 결과 객체의 형식이다.

예를 들어 Stream의 모든 요소를 List로 수집하는 ToListCollector라는 클래스는 아래와 같이 만들 수 있다.

supplier 메서드 : 새로운 결과 컨테이너 만들기

supplier 메서드는 수집 과정에서 빈 누적자 인스턴스를 만드는 파라미터가 없는 함수이다.

accumulator 메서드 : 결과 컨테이너에 요소 추가하기

accumulator 메서드는 리듀싱 연산을 수행하는 함수를 반환한다. 즉 누적자(스트림의 첫 n-1개 항목을 수집한 상태)와 n번째 요소를 함수에 적용한다 (제네릭 형식도 <A, T>이다).

finisher 메서드 : 최종 변환값을 결과 컨테이너로 적용하기

finisher 메서드는 스트림 탐색을 끝내고 누적자 객체를 최종 결과로 반환하면서 누적 과정을 끝낼 때 호출할 함수를 반환해야한다. ToListCollector와 같이 누적자 객체가 이미 최종 결과인 상황도 있다. 이럴경우 finisher함수는 항등 함수를 반환한다.

combiner 메서드 : 두 결과 컨테이너 병합

combiner는 스트림의 서로 다른 서브파트를 병렬로 처리할 때 누적자가 이 결과를 어떻게 처리할지 정의한다.

  • 병렬작업의 크기가 작으면 오히려 순차진행보다 성능이 떨어질 수 있어 적절하게 조절하는 것이 중요

characteristics 메서드

characteristics 메서드는 컬렉터의 연산을 정의하는 Characteristics 형식의 불변 집합을 반환한다.

  • UNORDERED : 리듀싱 결과는 스트림 요소의 방문 순서나 누적 순서에 영향을 받지 않는다.
  • CONCURRENT : 다중 스레드에서 accumulator 함수를 동시에 호출할 수 있으며 병렬 리듀싱을 수행할 수 있다. 컬렉터의 플래그에 UNORDERED를 함께 설정하지 않았다면 데이터 소스가 정렬되어 있지 않은 상황에서만 병렬 리듀싱을 수행할 수 있다.
  • IDENTITY_FINISH : finisher 메서드가 반환하는 함수는 단순히 identity를 적용할 뿐이므로 이를 생략할 수 있다. 따라서 리듀싱 과정의 최종 결과로 누적자 객체를 바로 사용할 수 있다. 또한 누적자 A를 결과 R로 안전하게 형변환할 수 있다.

Collector  만들기

  1. 클래스 시크니처 정의 public clasee PrimeNumbersCollectors implements collector<스트림요소형식, 누적자형식, 수집연산결과 형식>
  2. 리듀싱 연산 구현
    • supplier
    • accumulator
  3. 병렬 실행되도록 만들 수 있다면 만들기
  4. finisher와 characteristics 메서드 만들기
728x90

'스터디 책 정리 > 모던 자바 인 액션' 카테고리의 다른 글

병렬 데이터 처리와 성능  (0) 2023.09.12
스트림 활용  (0) 2023.09.11
스트림  (0) 2023.09.11