Java의 `Stream API`와 `for-loop` 간 성능 차이를 체험한 경험을 바탕으로, 그 원인과 상황별 선택 기준을 정리했다.
💡 문제 배경: 백준 1233번 - 주사위 문제
이 문제를 풀면서 두 버전의 코드를 제출했고, Stream API를 쓴 코드보다 for-loop를 쓴 코드가 유의미하게 더 빠르다는 걸 확인했다.
첫 번째 코드 (Stream 사용) ➝ 순위권 밖
두 번째 코드 (for-loop 사용) ➝ Java 제출 기준 2등 기록
이러한 차이가 생긴 원인이 궁금하여 분석해보았다.
🔍 코드 비교
✅ 첫 번째 코드 (Stream 사용)
int max = Arrays.stream(sumCnt).max().getAsInt();
- 배열의 최대값을 구할 때 Stream을 사용
- 간결하지만 성능은 떨어짐
✅ 두 번째 코드 (for-loop 사용)
int max = 0;
int maxIdx = 0;
for (int i = 0; i < sumCnt.length; i++) {
if (sumCnt[i] > max) {
max = sumCnt[i];
maxIdx = i;
}
}
- 최대값과 인덱스를 동시에 구함
- Stream보다 빠르게 실행됨
🧠 왜 Stream API가 느린 걸까?
1. JVM 최적화의 역사
- `for-loop`는 자바의 가장 기본적인 반복문으로 오랜 시간 동안 최적화되어 있음
- 반면 `Stream API`는 Java 8부터 등장 ➝ 아직 최적화 측면에서 불리
2. Primitive vs Wrapper (박싱/언박싱 비용)
- Stream은 제네릭 기반이라 `Stream<Integer>`처럼 Wrapper를 사용
- 이는 heap 영역을 거쳐야 하고, 오토박싱/언박싱이 발생 ➝ 성능 저하
- `IntStream` 같은 primitive stream이 있지만 여전히 for-loop보다 느릴 수 있음
3. 스트림 파이프라인 오버헤드
- `map`, `filter`, `reduce` 같은 중간 연산들이 람다식 기반 내부 클래스 생성
- 이로 인해 불필요한 객체 생성과 메서드 호출 오버헤드 발생 가능
4. 병렬 스트림의 동기화 비용
- `parallelStream()`은 멀티스레드를 활용하므로 동기화 비용 발생
- 간단한 연산에서는 오히려 성능이 떨어질 수 있음
✅ 그럼에도 Stream API를 쓰는 이유
항목 | for-loop | Stream API |
---|---|---|
성능 | ✅ 빠름 | ❌ 느림 |
코드 가독성 | ❌ 길어질 수 있음 | ✅ 간결함 |
데이터 크기 | ✅ 소규모 적합 | ✅ 대규모 처리 적합 |
병렬 처리 | ❌ 직접 구현 | ✅ parallelStream() 사용 가능 |
불변성 유지 | ❌ 변경 가능성 있음 | ✅ 함수형 스타일 |
요약
- 빠른 반복, 성능이 중요한 경우 ➝ `for-loop`
- 가독성과 선언형 프로그래밍, 병렬 처리 필요 ➝ `Stream API`
'공부 기록 > 자프링' 카테고리의 다른 글
[Spring] Controller, Service, Repository, Domain, DTO가 뭐야 (0) | 2025.06.22 |
---|---|
[Java] String to int, int to String 변환하기 (2) | 2025.05.11 |
[Java] LinkedList와 ListIterator에 대한 고찰 (1) | 2025.05.01 |