2023. 6. 6. 22:45ㆍJava
Stream.reduce
는 스트림의 요소들을 하나의 값으로 축약(reduction) 하는 데 사용되는 종료 연산입니다. 이 연산은 제공된 아이덴티티 값(identity)과 누적 함수(accumulator)를 사용해, 스트림의 모든 요소를 차례대로 처리하고 최종 결과를 반환합니다.
이 메서드는 주어진 두 인자를 받습니다:
- 아이덴티티 값 (identity): 누적 함수의 초기값으로, 축약 결과에 영향을 미치지 않는 값입니다. 예를 들어, 합계를 계산할 때 아이덴티티 값은 0, 곱셈을 할 때는 1이 될 수 있습니다.
- 누적 함수 (accumulator): 두 값을 입력받아 하나의 결과를 반환하는 함수입니다. 이 함수는 스트림의 각 요소와 이전 연산의 결과를 조합하여 새로운 값을 생성합니다.
reduce
는 다음과 같이 작동합니다:
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element);
return result;
요구 사항:
- 아이덴티티 값: 누적 함수에 대해 항등적(즉, 어떤 값과 연산해도 그 값이 변하지 않는 값)이어야 합니다. 예를 들어,
Integer
덧셈 연산의 항등값은0
, 곱셈 연산의 항등값은1
입니다. - 누적 함수: 이 함수는 결합법칙(associative)을 만족해야 합니다. 즉, 함수의 입력 순서에 상관없이 결과가 같아야 합니다. 예를 들어,
a + b
는(a + b) + c = a + (b + c)
의 결합법칙을 따릅니다.
동작 예시
- 스트림의 합계 계산:이 예시에서
reduce(0, (a, b) -> a + b)
는0
을 아이덴티티 값으로,(a, b) -> a + b
를 누적 함수로 사용하여 스트림의 모든 값을 더합니다.0
은 더하기 연산의 항등값이므로, 첫 번째 요소와 연산을 시작할 수 있습니다. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 누적 함수는 두 값을 더하는 역할을 하고, 아이덴티티 값은 0입니다. int sum = numbers.stream() .reduce(0, (a, b) -> a + b); System.out.println("합계: " + sum); // 출력: 합계: 15
- 스트림의 곱셈 계산:여기서
reduce(1, (a, b) -> a * b)
는1
을 아이덴티티 값으로 사용하여 스트림의 모든 값을 곱합니다.1
은 곱셈 연산의 항등값입니다. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 누적 함수는 두 값을 곱하는 역할을 하고, 아이덴티티 값은 1입니다. int product = numbers.stream() .reduce(1, (a, b) -> a * b); System.out.println("곱셈 결과: " + product); // 출력: 곱셈 결과: 120
- 스트림에서 최댓값 찾기:여기서는
Math.max(a, b)
가 두 값을 비교해 더 큰 값을 반환하는 역할을 합니다. 아이덴티티 값으로Integer.MIN_VALUE
를 사용하여, 모든 값과 비교했을 때 더 작은 값이 없도록 설정합니다. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 누적 함수는 두 값을 비교하여 더 큰 값을 반환합니다. int max = numbers.stream() .reduce(Integer.MIN_VALUE, (a, b) -> Math.max(a, b)); System.out.println("최댓값: " + max); // 출력: 최댓값: 5
- 문자열 스트림의 연결:이 경우, 빈 문자열
""
은 문자열 연결의 항등값이며, 스트림의 각 문자열을 차례대로 이어붙입니다. List<String> words = Arrays.asList("Hello", " ", "World", "!"); // 누적 함수는 두 문자열을 이어붙이는 역할을 하고, 아이덴티티 값은 빈 문자열입니다. String result = words.stream() .reduce("", (a, b) -> a + b); System.out.println("문자열 연결: " + result); // 출력: 문자열 연결: Hello World!
reduce
의 병렬 처리 가능성
reduce
는 스트림의 데이터를 병렬 처리할 수 있는 방식으로 설계되어 있습니다. 스트림의 각 부분을 별도로 축약하고, 나중에 병합할 수 있기 때문에, 병렬 스트림(parallelStream()
)에서도 안전하게 사용할 수 있습니다.
병렬 스트림 예시:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.reduce(0, (a, b) -> a + b);
System.out.println("병렬 합계: " + sum); // 출력: 병렬 합계: 15
이 코드에서는 스트림이 병렬로 처리되어 여러 스레드에서 나누어진 부분 집합을 동시에 처리한 후, 최종적으로 합산합니다.
예시
// 총 가격 계산 메서드
public BigDecimal getTotalPrice() {
return streamable.stream()
.map(Product::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
위 코드는 getTotalPrice
라는 메서드로, streamable
컬렉션에 있는 Product
객체들의 가격을 모두 합산하여 총 가격을 계산하는 기능을 합니다. 이 메서드는 Java Stream API와 BigDecimal
을 사용하여 각 Product
의 가격을 축약(reduce) 방식으로 더하고, 최종적으로 합산된 가격을 반환합니다.
코드를 단계별로 설명하면 다음과 같습니다:
1. streamable.stream()
streamable
은 Streamable<Product>
타입의 컬렉션입니다. 이 컬렉션은 Product
객체들의 리스트나 다른 Iterable
타입을 감싸고 있는 객체입니다.
.stream()
메서드를 호출하면, streamable
내부의 Product
객체들을 스트림으로 변환하여 스트림 API를 사용할 수 있게 합니다. 스트림 API를 사용하면 컬렉션 내의 요소들에 대해 반복적인 작업을 수행할 수 있습니다.
2. .map(Product::getPrice)
map
메서드는 스트림의 각 요소(이 경우는 Product
객체)에 대해 주어진 함수를 적용하는 역할을 합니다. 여기서 Product::getPrice
는 메서드 참조 방식으로 각 Product
객체의 getPrice()
메서드를 호출하여 가격(BigDecimal
타입)을 가져옵니다.
즉, streamable.stream()
으로 만들어진 Product
객체 스트림에서 각 Product
의 가격을 가져와서 가격 스트림으로 변환하는 작업을 수행합니다.
3. .reduce(BigDecimal.ZERO, BigDecimal::add)
reduce
메서드는 스트림의 모든 요소를 축약하여 단일 값으로 만드는 연산을 수행합니다. 이 경우, 스트림의 모든 가격을 더하는 방식으로 작동합니다.
reduce
메서드는 두 개의 인자를 받습니다:
- 초기값 (identity):
BigDecimal.ZERO
는 덧셈 연산에 사용되는 아이덴티티 값으로, 이는0
과 같은 역할을 합니다.BigDecimal.ZERO
는BigDecimal
타입에서 0을 나타냅니다. 아이덴티티 값은 첫 번째 연산의 시작점이 되며, 축약 중 연산에 영향을 미치지 않는 값입니다. - 누적 함수 (accumulator):
BigDecimal::add
는 두 개의BigDecimal
값을 더하는 함수입니다. 이는 스트림의 각 요소를 순차적으로 처리하면서 이전의 누적된 합계와 현재 요소의 가격을 더합니다.
reduce
의 동작은 다음과 같습니다:
- 처음에는 아이덴티티 값인
BigDecimal.ZERO
를 기준으로 스트림의 첫 번째BigDecimal
값(가격)과 더해집니다. - 그다음, 두 번째 가격과 누적된 합계를 더하고, 이를 스트림 끝까지 반복합니다.
- 최종적으로 모든
BigDecimal
값들이 더해져 총 가격을 반환합니다.
전체 코드 흐름
streamable
에서Product
객체들이 스트림으로 변환됩니다.- 각
Product
객체의 가격을getPrice()
메서드로 추출하여 가격 스트림을 생성합니다. reduce
메서드를 사용하여 모든 가격을 더한 총 가격을 계산합니다. 여기서 덧셈은BigDecimal
의add()
메서드를 사용하며, 처음에BigDecimal.ZERO
(0)부터 시작해서 각 가격을 차례대로 더합니다.- 마지막으로, 모든 가격이 더해진 총 가격(
BigDecimal
값)을 반환합니다.
예시 코드
class Product {
private BigDecimal price;
public Product(BigDecimal price) {
this.price = price;
}
public BigDecimal getPrice() {
return price;
}
}
public class Example {
public static void main(String[] args) {
List<Product> products = Arrays.asList(
new Product(new BigDecimal("19.99")),
new Product(new BigDecimal("5.49")),
new Product(new BigDecimal("3.79"))
);
Streamable<Product> streamable = Streamable.of(products);
BigDecimal totalPrice = streamable.stream()
.map(Product::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println("총 가격: " + totalPrice); // 출력: 총 가격: 29.27
}
}
주요 개념 요약:
- Stream:
stream()
메서드를 통해Product
객체들을 스트림으로 변환합니다. - map: 각
Product
객체에서 가격을 추출합니다. - reduce: 가격을 모두 더하여 하나의 값(총 가격)으로 축약합니다.
- BigDecimal.ZERO: 덧셈의 시작 값으로 사용됩니다.
- BigDecimal::add: 가격을 더하는 누적 함수로 사용됩니다.
이 코드는 여러 상품의 가격을 계산할 때 유용하며, BigDecimal
을 사용해 정확한 금액 계산을 처리합니다.
요약
reduce
는 스트림의 요소들을 하나의 값으로 축약하는 메서드입니다.- 아이덴티티 값은 연산에서 항등적이어야 하며, 누적 함수는 결합법칙을 만족해야 합니다.
- 병렬 처리에서도 안전하게 사용할 수 있으며, 여러 가지 축약 연산(합계, 곱셈, 최댓값/최솟값, 문자열 연결 등)에 활용할 수 있습니다.
이를 통해 스트림의 데이터를 손쉽게 축약하여 처리할 수 있습니다.
'Java' 카테고리의 다른 글
Callable & ExecutorService (0) | 2023.06.23 |
---|---|
Cloneable 인터페이스(06/12) (0) | 2023.06.06 |
Class & Instance Copy(06/12) (0) | 2023.06.06 |
native 키워드 (0) | 2023.06.06 |
record (0) | 2023.06.04 |