728x90
요약:reduce는 스트림의 요소를 하나의 값으로 접어(fold) 합치는 범용 집계 연산입니다.sum()·count()같은 전용 집계가 "미리 만들어진 reduce"라면,reduce는 합·곱·연결·최댓값 등 임의의 누적 규칙을 직접 작성할 수 있는 원형입니다. 이 글에서 항등값(identity)의 의미, 2-인자 vs 1-인자(Optional) 버전, 그리고 면접 포인트인 "항등값을 왜 주는가"를 정리합니다.
filter·map으로 데이터를 가공했다면, 결국 하나의 결과로 모아야 할 때가 많습니다. "전체 합", "총 곱", "모든 문자열 연결" 처럼요. 기초편 6편의 sum()은 합 전용이었지만, "요소 사이에 커스텀 연산을 끼워 누적" 하려면 reduce가 필요합니다.
reduce의 핵심은 초깃값(항등값)에서 시작해, 누산기(accumulator)로 요소를 하나씩 접어 넣는다는 것입니다. 합이면 0에서 시작해 +, 곱이면 1에서 시작해 *. 이 "시작값 + 결합 규칙" 패턴만 이해하면 어떤 집계든 표현할 수 있습니다.

문제 정의
문제. 정수 리스트[1,2,3,4,5]에 대해 ① 합, ② 곱, ③ 최댓값을reduce로 구하고, ④ 문자열 리스트를-로 연결하라.
- 입력:
List<Integer> nums = [1, 2, 3, 4, 5],List<String> words = ["Java", "Stream", "API"] - 기대 출력: 합
15, 곱120, 최댓값5, 연결Java-Stream-API
전통 방식(for-loop) — 그리고 스트림이 나은 이유
import java.util.List;
public class Traditional {
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3, 4, 5);
int sum = 0; // 초깃값(항등값)
for (int n : nums) {
sum = sum + n; // 누적 규칙
}
System.out.println(sum);
}
}
for 루프의 "누적 변수 + 반복 갱신" 패턴, 익숙하시죠? 바로 이것이 reduce가 추상화한 구조입니다. 초깃값 0 과 결합 규칙 sum + n 두 가지만 바꾸면 합이 곱도, 연결도 됩니다. reduce는 그 두 조각만 인자로 받습니다.
스트림 해법 — reduce

import java.util.List;
import java.util.Optional;
public class ReduceExample {
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3, 4, 5);
// ① 합 — 항등값 0, 결합 규칙 더하기
int sum = nums.stream().reduce(0, Integer::sum);
System.out.println(sum);
// ② 곱 — 항등값 1, 결합 규칙 곱하기
int product = nums.stream().reduce(1, (a, b) -> a * b);
System.out.println(product);
// ③ 항등값 없는 버전 → Optional 반환
Optional<Integer> max = nums.stream().reduce(Integer::max);
System.out.println(max.get());
// ④ 문자열을 '-'로 연결 (빈 문자열에서 시작)
List<String> words = List.of("Java", "Stream", "API");
String joined = words.stream()
.reduce("", (acc, w) -> acc.isEmpty() ? w : acc + "-" + w);
System.out.println(joined);
}
}
한 줄씩 설명
reduce(0, Integer::sum)— 첫 인자0이 항등값(초깃값), 둘째 인자Integer::sum이 누산기입니다.0부터 시작해 요소를 차례로 더해15가 됩니다.- **
reduce(1, (a, b) -> a b)— 곱은 항등값이1* 이어야 합니다(0으로 시작하면 전부 0).11234*5 = 120. reduce(Integer::max)— 항등값을 생략하면 1-인자 버전이 되어Optional<Integer>를 반환합니다. 스트림이 비면 "결과 없음"일 수 있기 때문이죠.max.get()으로5를 꺼냅니다.reduce("", (acc, w) -> ...)— 문자열 연결도reduce로 가능합니다. 다만 구분자 연결은 다음 편에서 볼Collectors.joining이 훨씬 간결합니다(여기선 원리 이해용).
실행 결과
15
120
5
Java-Stream-API
변형·함정·주의
- ⚠ 항등값(identity)은 "연산에 영향을 주지 않는 값". 합은
0, 곱은1, 문자열 연결은"". 곱에0을 주면 결과가 전부0이 되는 대표적 실수입니다. - 1-인자 vs 2-인자. 항등값을 주면 결과 타입이 그대로(
int등)지만, 항등값을 생략하면 빈 스트림 대비로Optional을 반환합니다. 빈 스트림 가능성이 있으면 1-인자 버전이 안전합니다. - 전용 집계가 있으면 그걸 써라. 단순 합·평균은
mapToInt(...).sum()/average()가 더 읽기 쉽고 빠릅니다.reduce는 표준 집계로 표현 안 되는 커스텀 규칙에 쓰는 것이 정석입니다. - 병렬 스트림의 3-인자
reduce.reduce(identity, accumulator, combiner)형태는 병렬 처리 시 부분 결과를 합치는combiner를 받습니다. 병렬에서는 항등값·결합 규칙이 결합법칙(associative) 을 만족해야 결과가 정확합니다.
관련 면접 질문
- Q1.
reduce에서 항등값(identity)의 역할은?
→ 누적의 초깃값이자, 빈 스트림일 때 반환되는 기본값입니다. 연산의 항등원이어야 하며(합=0, 곱=1), 병렬 처리 시 각 조각의 시작값으로도 쓰입니다.
- Q2. 항등값 없는
reduce가Optional을 반환하는 이유는?
→ 초깃값이 없으면 빈 스트림일 때 내놓을 값이 없기 때문입니다. 그래서 "값이 없을 수 있음"을 Optional로 표현합니다.
시리즈 다시보기
- ⬆️ 📎 목차 허브 — 자바 스트림 코딩 문제 41선 총정리 (0편으로 돌아가기)
- ⬅️ 📎 이전 편 — [자바 스트림 11] flatMap으로 중첩 평탄화
- ➡️ 📎 다음 편 — [자바 스트림 13] groupingBy로 그룹핑 (발행 시 연결)
- 📎 함께 읽기 — Java 25 LTS 새 기능 총정리 (최신 자바 문법 레퍼런스)
누적의 원리를 익혔으니, 다음 편부터는Collectors의 진짜 핵심이 시작됩니다. 먼저 코테·면접 출제율 1위groupingBy로 그룹핑입니다.
'쿤즈 Dev > Java' 카테고리의 다른 글
| [자바 스트림 13] groupingBy로 그룹핑 — SQL GROUP BY를 코드로 (0) | 2026.10.30 |
|---|---|
| [자바 스트림 11] flatMap으로 중첩 평탄화 — 리스트 속 리스트를 한 줄로 (0) | 2026.10.27 |
| [자바 스트림 10] min·max 최솟값·최댓값 — 기준 하나로 극값 뽑기 (0) | 2026.10.26 |
| 자바 스트림, 이렇게 쓰면 느려진다 — 성능 함정 8가지와 개선법 (Java 21) (0) | 2026.10.25 |
| [자바 스트림 9] limit·skip 페이징 — 앞에서 자르고 건너뛰기 (0) | 2026.10.25 |
댓글