
Java 스트림(Stream) 완벽 가이드
Java 8부터 도입된 Stream API는 컬렉션 데이터를 함수형 스타일로 처리할 수 있도록 도와주는 강력한 도구입니다. 반복문 없이 선언적으로 데이터를 필터링, 매핑, 정렬, 집계하는 등 다양한 연산을 간결하게 구현할 수 있습니다.
1. Stream이란?
Stream은 컬렉션(List, Set 등)의 요소들을 하나씩 처리하며, 파이프라인 방식으로 연산을 연결해 구성할 수 있는 데이터 처리 흐름입니다. 데이터 자체를 변경하지 않고, 일관성 있는 결과를 반환합니다.
2. Stream 특징
- 선언형 코드: for문 없이 간결한 문법
- 중간 연산과 최종 연산 구분: filter, map 등은 중간 연산 / collect, count 등은 최종 연산
- 지연 연산(Lazy Evaluation): 최종 연산이 호출될 때까지 실행되지 않음
- 병렬 처리 지원: parallelStream()으로 멀티코어 활용
3. 기본 사용법
List names = Arrays.asList("Kim", "Lee", "Park");
names.stream()
.filter(name -> name.startsWith("K"))
.forEach(System.out::println);
위 예제는 "K"로 시작하는 이름만 필터링하여 출력합니다.
4. 주요 연산 종류
4.1 filter()
조건에 맞는 요소만 추출
list.stream().filter(x -> x > 10);
4.2 map()
요소 변환
list.stream().map(String::toUpperCase);
4.3 sorted()
정렬
list.stream().sorted();
4.4 collect()
결과 수집
List result = list.stream()
.filter(x -> x.length() > 3)
.collect(Collectors.toList());
4.5 reduce()
요소 누적 연산
int sum = list.stream().reduce(0, Integer::sum);
5. 병렬 스트림 (parallelStream)
멀티코어 환경에서 성능 향상을 위해 병렬 처리 가능
list.parallelStream().forEach(System.out::println);
단, 데이터 양이 작거나 정렬 순서가 중요할 경우에는 stream() 사용 권장
6. 실무 예제 - 사용자 리스트 필터링
class User {
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
List users = Arrays.asList(
new User("Kim", 30),
new User("Lee", 25),
new User("Park", 40)
);
List result = users.stream()
.filter(u -> u.age >= 30)
.map(u -> u.name)
.collect(Collectors.toList());
7. 주의할 점
- stream은 재사용 불가: 한 번 소비하면 다시 사용할 수 없음
- 성능 고려: 너무 많은 중간 연산은 지연 발생
- 상태 유지 연산 지양: 외부 상태 변경하지 않도록 작성
8. 결론
Stream API는 Java 컬렉션을 선언형 방식으로 간결하게 처리할 수 있도록 도와줍니다. filter, map, reduce 등 핵심 연산을 이해하고 적절히 조합하면 코드 품질과 생산성이 크게 향상됩니다.