1. 스트림의 내부 동작 원리
(1) 지연 평가(Lazy Evaluation)의 핵심 메커니즘
- 중간 연산의 실제 실행 시점: 최종 연산이 호출될 때까지 연산이 지연됨
- 최적화 기회: 불필요한 계산 회피 (예:
limit()과filter()조합) - 예시:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); numbers.stream() .filter(n -> { System.out.println("Filtering " + n); return n % 2 == 0; }) .map(n -> { System.out.println("Mapping " + n); return n * 2; }) .limit(1) .forEach(System.out::println); // 출력: Filtering 1 → Filtering 2 → Mapping 2 → 4
(2) 스트림 파이프라인의 단계별 처리
graph LR
A[Source] --> B[Intermediate Op 1]
B --> C[Intermediate Op 2]
C --> D[...]
D --> E[Terminal Op]
2. 고급 스트림 기법
(1) 커스텀 스트림 생성
- Stream.generate(): 무한 스트림 생성
- Stream.generate(Math::random) .limit(5) .forEach(System.out::println);
- Stream.iterate(): 초기값과 조건 기반 스트림
- Stream.iterate(0, n -> n < 10, n -> n + 2) .forEach(System.out::println); // 0, 2, 4, 6, 8
(2) 병렬 스트림 최적화
- Spliterator 커스맨십:
class CustomSpliterator<T> implements Spliterator<T> { // 분할 로직과 특성 정의 }
- 병렬 처리 시 주의점:
- 공유 자원 사용 금지 (Thread Safety)
- 순서 보장:
forEachOrdered()사용List<Integer> nums = Arrays.asList(1, 2, 3, 4); nums.parallelStream() .forEachOrdered(System.out::print); // 1234
(3) 성능 측정 및 최적화
- JMH(Java Microbenchmark Harness) 활용:
@Benchmark @BenchmarkMode(Mode.AverageTime) public void streamBenchmark() { IntStream.range(0, 1_000_000) .filter(n -> n % 2 == 0) .sum(); } - 병렬 vs 직렬 성능 비교:
// 직렬: 15ms, 병렬: 8ms (8코어 CPU 기준)
3. 스트림과 Optional의 고급 조합
(1) Optional 스트림 처리
List<Optional<String>> options = Arrays.asList(
Optional.of("Java"), Optional.empty(), Optional.of("Stream")
);
List<String> values = options.stream()
.flatMap(Optional::stream)
.toList(); // ["Java", "Stream"]
(2) 중첩 Optional 처리
Optional<Optional<Double>> nestedOpt = Optional.of(Optional.of(3.14));
Double result = nestedOpt.flatMap(Function.identity())
.orElse(0.0); // 3.14
4. 스트림의 상태 관리
(1) 상태 있는 중간 연산
sorted(): 전체 요소를 버퍼에 저장 후 정렬distinct(): 이전 요소 추적 필요- 주의: 병렬 스트림에서 상태 관리 복잡성 증가
(2) 스트림의 불변성 원칙
- 원본 데이터 변경 금지:
List<String> list = new ArrayList<>(Arrays.asList("A", "B")); list.stream() .peek(s -> list.add("C")) // ⚠️ ConcurrentModificationException 발생 가능 .forEach(System.out::println);
5. 고급 수집기(Collectors) 활용
(1) 커스텀 수집기 구현
Collector<String, StringBuilder, String> customCollector =
Collector.of(
StringBuilder::new,
(sb, s) -> sb.append(s).append(","),
(sb1, sb2) -> sb1.append(sb2),
sb -> sb.deleteCharAt(sb.length()-1).toString()
);
String result = Stream.of("A", "B", "C")
.collect(customCollector); // "A,B,C"
(2) 다중 레벨 그룹화
Map<Department, Map<Boolean, List<Employee>>> grouped = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.partitioningBy(e -> e.getSalary() > 5000)
));
6. 스트림 디버깅 기법
(1) 중간 결과 확인
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
nums.stream()
.peek(n -> System.out.println("Original: " + n))
.filter(n -> n % 2 == 0)
.peek(n -> System.out.println("Filtered: " + n))
.map(n -> n * 2)
.forEach(System.out::println);
(2) 스택 트레이스 분석
- 스트림 연산 체인 식별:
Exception in thread "main" java.lang.NullPointerException at MyClass.lambda$main$0(MyClass.java:15) at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
7. 성능 최적화 전략
(1) 기본형 특화 스트림 활용
// 박싱 오버헤드 제거
IntStream.rangeClosed(1, 1_000_000)
.average()
.orElse(0); // DoubleStream, LongStream 동일
(2) 병렬 처리 최적화 가이드
- 적용 조건:
- 데이터 크기 ≥ 10,000 요소
- 연산이 CPU 집약적일 때
- 소스가 쉽게 분할 가능할 때 (ArrayList vs LinkedList)
- 병렬 처리 회피:
- 공유 자원 사용 시
- 순서 의존적 연산
결론: 프로덕션 레벨 스트림 활용 전략
- 데이터 특성에 맞는 스트림 선택: 기본형 스트림 우선 사용
- 병렬 처리 신중 적용: 성능 테스트 필수
- 복잡한 파이프라인 모듈화: 가독성 유지
- 스트림+Optional 조합: Null-safe 코드 설계
- 성능 모니터링: JFR(Java Flight Recorder) 활용
// 최적화된 예시: 100만 개 데이터 처리
long start = System.nanoTime();
double avg = IntStream.range(0, 1_000_000)
.parallel()
.filter(n -> n % 3 == 0)
.average()
.orElse(0);
long duration = (System.nanoTime() - start)/1_000_000;
System.out.printf("평균: %.2f, 처리시간: %dms%n", avg, duration);
스트림의 고급 기능을 마스터하면 데이터 처리 로직의 효율성과 유지보수성을 획기적으로 개선할 수 있습니다. 하지만 남용 시 오히려 성능 저하와 가독성 문제를 초래할 수 있으므로, 항상 실제 사용 사례와 성능 측정을 통해 최적의 접근 방식을 선택해야 합니다.
'Programming Language > Java' 카테고리의 다른 글
| Exception handling (0) | 2025.03.05 |
|---|---|
| Optional (예제 주의) (0) | 2025.03.05 |
| Stream (1) | 2025.03.05 |
| 람다 표현식 (Lambda Expressions) (0) | 2025.03.05 |
| [Collection] PriorityQueue Guide (0) | 2025.03.04 |
