1. Question

스트림 처리 연산 리듀싱(reducing)이란 무엇인가?

2. Answer

Java의 stream API에서 reduce 연산은 스트림의 요소들을 하나의 값으로 결합하는 데 사용된다. 이 연산은 스트림의 모든 요소를 반복적으로 처리하여 단일 결과를 생성하는 ‘축소’ 작업을 수행한다. reduce는 함수형 프로그래밍에서 널리 사용되는 패턴으로, 복잡한 데이터 구조를 간단한 형태로 변환하는 데 매우 유용하다.

reduce 연산은 두 가지 주요 구성 요소를 갖는다.

  • 초기값: 축소 작업의 시작점으로, 첫 번째 요소가 처리되기 전의 초기 값이다.

  • 누적 함수: 두 요소를 받아서 하나의 값으로 결합하는 함수이다. 이 함수는 스트림의 요소들을 순차적으로 처리하면서 결과를 누적한다.

reduce를 이용해서 다음ㅊ머럼 스트림의 모든 요소를 더할 수 있다.

int sum1 = numbers.stream().reduce(0, (a, b) -> a + b)
// 자바 8에서는 Integer 클래스에 두 숫자를 더하는 정적 sum 메서드를 제공한다.
int sum2 = numbers.stream().reduce(0, Integer::sum);

초깃값을 받지 않도록 오버로드된 reduce도 있다. 그러나 이 reduceOptional 객체를 반환한다. 스트림에 아무 요소도 없는 상황이라면 초기값이 없으므로 reduce는 합계를 반환할 수 없다. 따라서 합계가 없음을 가리킬 수 있도록 Optional 객체로 감싼 결과를 반환하는 것이다.

Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));

reduce를 이용해서 스트림의 최댓값을 찾을 수도 있다.

Optional<Integer> max = numbers.stream().reduce(Integer::max);

3. Detail

A. reduce 병렬화

reduce 연산은 병렬 처리에도 매우 적합하다. stream API.parallel() 메서드를 통해 스트림을 병렬 스트림으로 쉽게 변환할 수 있으며, reduce는 이러한 병렬 스트림과 잘 동작한다.

int sum = numbers.parallelStream().reduce(0, Integer::sum);
  • 분할 정복: 병렬 스트림은 데이터를 여러 부분으로 분할하고, 각 부분을 별도의 스레드에서 처리한 후, 결과를 하나로 결합한다. reduce 연산은 이러한 분할 정복 방식에 잘 맞는다.

  • 성능 향상: 병렬 스트림과 reduce를 사용하면, 특히 데이터가 많고 CPU 코어가 여러 개인 환경에서 성능을 크게 향상시킬 수 있다.

  • 스레드 안정성: reduce는 불변성을 유지하므로, 병렬 처리 시에도 데이터의 일관성과 스레드 안전성을 보장한다.

4. Reference

  • “모던 자바 인 액션” (저자: 라울-게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로프트)

태그:

카테고리:

업데이트:

댓글남기기