모던 자바 인 액션 10. 스트림 활용(4) - 리듀싱
JAVA/모던 자바 인 액션

모던 자바 인 액션 10. 스트림 활용(4) - 리듀싱

리듀싱

이때까지 스트림 활용들을 보면 복잡한 기능은 구현하지 못했습니다.

  • 스트림 필터링하면서 데이터 수 세기
  • 모든 음식 값의 합계 등등...

이번에는 리듀스 기능을 이용해 복잡한 연산을 해봅시다!

리듀싱이란 원하는 타입이나 결과가 나올 때까지 스트림의 모든 요소를 반복적으로 처리하는 연산을 말합니다.

(함수형 프로그래밍 언어에서는 폴드라고도 부릅니다.)

리듀스 연산

먼저 리스트 안의 요소 합을 구하는 예제를 통해 리듀스를 알아가봅시다.

코드는 간단합니다. 

private int forEachSum(List<Integer> numbers) {
    int sum = 0;
    for (int x : numbers) {
        sum += x;
    }
    return sum;
}

 

반복(루프) 문 같은 경우에는, 리스트의 처음부터 끝까지(지정된 범위)를 반복을 합니다.

리듀스 역시 스트림 처음부터 끝까지 반복을 합니다! (정확히 말하자면, 요소가 하나 남을 때까지 반복합니다.)

private static int reduceSumV1(List<Integer> numbers) {
    return numbers.stream()
        .reduce(0, (a, b) -> a + b);		// reduce(초기값, 람다)
}


private static int reduceSumV2(List<Integer> numbers) {
    return numbers.stream()
        .reduce(0, Integer::sum);		// reduce(초기값, 람다)
}

리듀스에는 초기값과 함수가 필요한 것을 알 수 있습니다.

그리고 람다가 가능하면, 메서드 참조까지 가능합니다! 

추가로 초기값이 생략된 리듀스 연산에는 초기값이 없기 때문에,  Optional객체로 반환이 됩니다.

reduce 연산 과정

reduce는 스트림이 한 개로 줄어들 때까지 각 요소를 반복해서 연산한다고 설명했습니다.

다음은 요소의 합을 구하는 과정입니다. 

그림1 요소의 합을 구하는 과정

다른 연산들도 다 같은 과정으로 이루어집니다.

 

스트림 내부 상태

다양한 스트림 연산을 수행할 때, 내부 상태를 고려해야 합니다.

 

'map( )', 'filter( )' 등은 입력 스트림에서 각 요소를 받아 결과를 출력 스트림으로 보냅니다.

이들은 보통 상태가 없고, 이를 내부 상태를 갖지 않는 연산(stateless operation)이라고 부릅니다.

이와 다르게 'reduce', 'sum', 'max' 같은 연산은 결과를 누적할 내부 상태가 필요합니다!

'sorted'와 'distinct' 역시 내부 요소를 가지고 있습니다.

(내부 정렬이나, 중복을 제거하기 위해서는 과거 이력을 알아야 하기 때문에 당연한 일.)

 

참고 자료

  • 모던 자바 인 액션
  • JAVA Reference