Java - Stream.reduce() 사용 방법 및 예제

Java 8에서 도입된 Stream의 reduce() 사용 방법 및 예제를 소개합니다.

1. Stream.reduce()

Stream.reduce(accumulator) 함수는 Stream의 요소들을 하나의 데이터로 만드는 작업을 수행합니다.

예를 들어, Stream에서 1부터 10까지 숫자가 전달될 때, 이 값을 모두 합하여 55의 결과가 리턴되도록 만들 수 있습니다. 여기서 연산을 수행하는 부분은 accumulator 함수이며, 직접 구현해서 인자로 전달해야 합니다.

아래는 reduce()를 사용하여 Stream에서 전달되는 요소들의 숫자를 모두 합하는 예제입니다.

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> sum = numbers.reduce((x, y) -> x + y);
sum.ifPresent(s -> System.out.println("sum: " + s));

Output:

sum: 55

위의 예제를 설명하면서 reduce()의 동작 방식에 대해서 소개하겠습니다.

reduce()는 인자로 BinaryOperator 객체를 받는데, BinaryOperator는 T 타입의 인자 두개를 받고 T 타입의 객체를 리턴하는 함수형 인터페이스입니다.

BinaryOperator는 (total, n) -> total + n와 같은 형식으로 인자가 전달되는데요. Stream의 1이 전달될 때, total(0) + n(1) = 1와 같이 계산되고, 여기서 리턴되는 1이 다음에 Stream에서 2가 전달될 때 total로 전달됩니다. 따라서, total(1) + n(2) = 3이 됩니다.

다시 정리하면 아래와 같이 연산되면서, 마지막에는 1에서 10까지의 숫자를 더한 55가 리턴됩니다.

  • total(0) + n(1) = 1
  • total(1) + n(2) = 3
  • total(3) + n(3) = 6
  • total(6) + n(4) = 10
  • total(10) + n(5) = 15
  • total(15) + n(6) = 21
  • total(21) + n(7) = 28
  • total(28) + n(8) = 36
  • total(36) + n(9) = 45
  • total(45) + n(10) = 55

1.1 메소드 레퍼런스로 구현

(x, y) -> x + y와 같이 합을 계산하는 함수는, JDK에서 Integer.sum(a, b) 라는 기본 함수를 제공합니다.

여기서 메소드 레퍼런스를 이용하면, Integer::sum 처럼 더 짧은 코드로 동일한 결과를 리턴하는 코드를 구현할 수 있습니다.

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> sum = numbers.reduce(Integer::sum);
sum.ifPresent(s -> System.out.println("sum: " + s));

2. 초기값이 있는 Stream.reduce()

위의 예제에서는 total의 초기 값이 0이였습니다.

하지만 Stream.reduce(init, accumulator) 처럼 초기 값을 인자로 전달할 수 있습니다.

위의 예제에서 초기 값이 10으로 설정하면, '10 + 1 + 2 + 3... 10'과 같이 연산을 합니다.

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.reduce(10, (total, n) -> total + n);
System.out.println("sum: " + sum);

Output:

sum: 65

3. reduce()의 병렬 처리

Stream.parallel()은 Stream 연산을 병렬 처리로 수행하도록 합니다. 즉, parallel()과 함께 reduce()를 사용하면 순차적으로 연산을 수행하지 않고 여러개의 연산을 동시에 진행하고, 그 작업들을 다시 병합하여 최종적으로 1개의 결과를 생성합니다.

예를 들어, (1 + 2) + (3 + 4) + ... + (9 + 10) 처럼 두개씩 묶어서 먼저 계산하고, 그 결과들을 다시 계산할 수 있습니다.

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.parallel().reduce(0, (total, n) -> total + n);
System.out.println("sum: " + sum);

Output:

sum: 55

하지만 빼기 연산의 경우 병렬처리는 순차적인 처리(병렬이 아닌)와 결과가 다릅니다. 아래 코드를 실행해보면 -55가 아니라 -5가 리턴됩니다. 결과가 다른 이유는 (1 - 2) - (3 - 4) - ... - (9 - 10) 처럼 연산이 수행되면서 순차적으로 연산하는 것과 결과가 달라지기 때문입니다.

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.parallel().reduce(0, (total, n) -> total - n);
System.out.println("sum: " + sum);

Output:

sum: -5

병렬 처리 과정에서 reduce()를 사용할 때는 위와 같은 문제가 없는지 확인을 해야 합니다.

4. 병렬 처리에서 reduce()는 순차적으로 처리

병렬처리에서 연산 순서에 따라 발생하는 문제를 해결하기 위해, 아래 예제와 같이 다른 규칙을 추가할 수 있습니다.

위의 예제와 비교해보면 (total1, total2) -> total1 + total2가 추가되었는데, 병렬로 처리된 결과들의 관계를 나타냅니다. 다시 설명하면, 첫번째 연산과 두번째 연산은 합해야 한다는 규칙을 추가한 것인데요. 이렇게 규칙을 추가하면, 첫번째 연산의 결과가 다음 연산에 영향을 주기 때문에 reduce()는 작업을 나눠서 처리할 수 없게 됩니다.

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.reduce(0,
        (total, n) -> total - n,
        (total1, total2) -> total1 + total2);
System.out.println("sum: " + sum);

Output:

sum: -55
Loading script...

Related Posts

codechachaCopyright ©2019 codechacha