모던 자바 인 액션 13. 스트림을 이용한 데이터 수집(2)
JAVA/모던 자바 인 액션

모던 자바 인 액션 13. 스트림을 이용한 데이터 수집(2)

Collector 인터페이스

이번에는 스트림의 과정을 정확하게 이해하기 위해 Collector 인터페이스를 살펴봅시다

그림1 Collector 인터페이스

여기서 T는 수집될 스트림 항목의 제네릭 형식 / A는 누적자(중간 결과를 누적하는 객체 형식) / R은 연산 결과 객체의 형식을 뜻합니다

 

1. supplier( ) : 새로운 결과 컨테이너 생성

그림 2 supplier( ) 메서드

사진에서 보이다시피 '새로운 mutable 한 결과 컨테이너를 생성하고 반환하는 메서드'입니다.

빈 가변의 결과 컨테이너   ->    빈 누적자 인스턴스를 만드는 함수

이렇게 이해하시면 좋습니다.

 

 

2. accumulator( ) : 결과 컨테이너에 요소 추가

그림 3 accumulator( ) 메서드

누적의 의미를 가지는 'accumulator( )' 메서드입니다.

값을 mutable 한 결과 컨테이너에 넣는 함수

여기서 mutable한 결과 컨테이너는 'supplier( )'에서 생성한 컨테이너입니다.

메서드에 제공되는 함수의 연산 결과를, 컨테이너에 넣는다고 생각하시면 좋습니다.

여기서 mutable 한 이유는, 누적자 내부 상태가 함수에 따라 바뀌기 때문입니다.

리듀싱을 떠올리시면 좋습니다. (리듀싱 연산을 수행하는 함수를 반환하는 메서드)

 

3. combiner( )

그림 4 combiner( ) 메서드

두 개의 부분 결과를 받아서 병합을 실시
한 인수 상태를 다른 인수로 바꾸어 반환 or 새로운 결과의 컨테이너를 반환
정리 : 두 개의 부분 결과를 결합된 결과로 반환하는 함수.

우리가 이때까지 다룬 순차적 처리 말고, 병렬로 처리해야 하는 경우도 생깁니다.

병렬로 처리 후 이제 결과를 하나로 합쳐야 하는데, 이때 사용하는 메서드가 바로 'combiner( )'입니다.

(서브 파트들의 연산 결과를 합칠 때 사용)

 

4. finisher( )

그림 5 combiner( ) 메서드

 

A [누적자 타입(객체)]R [최종 결과]로 최종 변환하는 것을 수행한다.
Return : 중간 결과를 최종 결과로 변환하는 함수

누적 과정(리듀싱)이 끝이 나면 연산을 종료하면서, 최종 결과로 변환을 반환해야 합니다.

'finisher( )'가 이를 수행하는 메서드입니다.

 

 

5. characteristics( )

그림 6 combiner( ) 메서드

Collector의 연산을 정의하는 Characteristics 형식의 Immuatble 한 집합을 반환합니다.

여기서 Characteristics는 스트림의 최적화 힌트를 제공하는 역할을 합니다. (병렬 여부, 최적화 여부)

그림 7 Characteristics

CONCURRENT

결과 컨테이너가 여러 스레드의 동일한 결과 컨테이너와 동시에 호출되는 누적자 함수를 지원한다. (병렬 지원)
CONCURRENT 수집기도 UNORDERED가 아니면 정렬되지 않은 데이터 소스의 경우에만 동시에 수행

먼저 다중 스레드에서 accumulator 함수를 동시에 호출할 수 있으며, 병렬 리듀싱을 수행하는 CONCURRENT입니다.

컬렉터의 플래그에 UNORDERED를 함께 설정하지 않았다면, 데이터 소스가 정렬되어 있지 않은 상황에서만 병렬 리듀싱을 수행할 수 있습니다.

 

 

UNORDERED

수집 작업이 입력되는 요소의 순서를 유지하기 위해 커밋하지 않는다.
결과 컨테이너에 Set과 같은 순서가 없는 경우에 해당될 수 있습니다.

리듀싱의 결과는 스트림 요소의 방문 순서나 누적 순서에 영향을 받지 않음을 뜻합니다.

 

IDENTITY_FINISH

finisher( ) 메서드는 단지 identity 메서드이기 때문에 생략할 수 있다.
설정된 경우 A에서 R로의 검사 되지 않은 캐스트가 성공해야 합니다.

말 그대로 'finisher( )' 메서드가 반환하는 함수는 단순히 파라미터 타입을 받아 반환하는 'identity( )'메서드 이기 때문에 생략이 가능하다는 말입니다. 

이 말인즉슨,

 

'finisher( )' 이 생략 가능하다 = 누적자 객체를 리듀싱 과정의 최종 결과로 사용할 수 있다.

 

입니다! 누적자에서 결과로 변경될 때 형 변환이 따로 필요 없을 경우 사용합니다.

 

 

최종적으로 Collector의 연산 과정을 살펴봅시다.

그림 8 순차적 리듀싱

여기서 병렬 리듀싱이 되어버린다면 이렇게 될 것입니다.

그림 9 병렬 리듀싱

 

복잡해 보이지만 기본적인 골자는 똑같습니다!

 

참고 자료

  • 모던 자바 인 액션
  • JAVA Reference

 

Collector (Java Platform SE 8 ) (oracle.com)

 

Collector (Java Platform SE 8 )

A mutable reduction operation that accumulates input elements into a mutable result container, optionally transforming the accumulated result into a final representation after all input elements have been processed. Reduction operations can be performed ei

docs.oracle.com