공부 기록/CS

[백엔드/Java] Stream vs for-loop 성능 비교

lelezhong 2025. 4. 21. 17:00

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`