자바 8에서 도입된 스트림(Stream) API는 컬렉션 데이터를 선언적으로 처리할 수 있는 강력한 도구다.
필터링, 매핑, 축소 등 복잡한 데이터 처리를 파이프라인 구조로 구현할 수 있다.
기본 필터링 및 매핑
가장 흔히 사용되는 중간 연산인 filter와 map을 활용한 예제다.
import java.util.*;
import java.util.stream.*;
public class StreamBasic {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 'a'를 포함하는 과일을 대문자로 변환하여 리스트로 수집
List<String> result = fruits.stream()
.filter(f -> f.contains("a"))
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(result); // [APPLE, BANANA, DATE]
}
}
데이터 축소 (Reduction)
reduce를 사용하여 스트림의 요소를 하나의 결과값으로 합칠 수 있다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 모든 숫자의 합계 계산
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
// 최댓값 찾기
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
System.out.println("Sum: " + sum); // 15
그룹화 및 통계 (Grouping & Statistics)
Collectors를 활용하여 데이터를 그룹화하거나 통계 정보를 추출할 수 있다.
class Product {
String category;
int price;
// 생성자 및 Getter 생략
}
// 카테고리별 제품 그룹화
Map<String, List<Product>> byCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
// 가격 통계 정보 추출
IntSummaryStatistics stats = products.stream()
.collect(Collectors.summarizingInt(Product::getPrice));
System.out.println("Average Price: " + stats.getAverage());
스트림의 특징
- 원본 보존: 스트림은 데이터를 읽기만 할 뿐 원본 컬렉션을 변경하지 않는다.
- 지연 연산 (Lazy Evaluation): 최종 연산이 호출되기 전까지 중간 연산은 실행되지 않는다.
- 일회용: 한 번 최종 연산을 수행한 스트림은 재사용할 수 없다.
P.S
스트림 API는 반복문 위주의 명령형 코드를 선언형 코드로 전환하여 가독성과 유지보수성을 높인다.
람다식과 결합하여 데이터 처리 로직을 간결하게 표현할 수 있는 것 같다.