Stream이란?
자바 스트림은 순서대로 적용되고 변경할 수 없는 함수 모음을 추상화한 것이다. 스트림과 컬렉션의 가장 중요한 차이점은 스트림이 데이터를 보유하지 않는다는 것이다. 데이터에 대해 작동하는 기능만 지정할 수 있으며, 스트림에서 작업을 수행하면 원본 스트림에 영향을 미치지 않는다.
스트림 구조
스트림은 데이터가 흐르는 파이프라인과 데이터에서 작동하는 기능을 나타낸다. 이 파이프라인은 스트림 소스, 0개 이상의 중간 작업, 터미널 작업으로 구성된다.
List numbers = Arrays.asList(1, 2, 3, 4);
List result = numbers.stream()
.filter(e -> (e % 2) == 0)
.map(e -> e * 2)
.collect(toList());주요 스트림 작업
Java 8 이상에서는 stream() 메서드를 호출하여 모든 컬렉션에서 스트림을 얻을 수 있다.
| 메서드 | 설명 |
|---|---|
| filter() | 조건에 맞는 요소만 포함하는 새 스트림을 반환 |
| map() | 스트림 요소를 다른 것으로 변환 |
| reduce() | 스트림을 단일 요소로 축소 |
| collect() | 스트림을 List 같은 구체적인 컬렉션으로 변환 |
주의사항
공유 상태 변형 문제
// 잘못된 예시 - 동시성 문제 발생 가능
List names = new ArrayList();
people.stream()
.filter(p -> p.getGender() == Gender.FEMALE)
.map(Person::getName)
.forEach(name -> names.add(name)); // 위험스트림이 병렬인 경우 공유 상태에 요소를 동시에 추가하면 오류가 발생할 수 있다.
올바른 방법 - collect() 사용
List names = people.stream()
.filter(p -> p.getGender() == Gender.FEMALE)
.map(Person::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());터미널 작업 누락 문제
중간 작업만 지정하고 터미널 작업이 누락되면 아무것도 실행되지 않는다. 터미널 작업이 있을 때만 중간 작업이 실행된다.
// 출력 없음 - 터미널 작업 누락
Stream.of("Cathy", "Alba", "Beth")
.filter(s -> {
System.out.println("filter " + s);
return true;
});중간 작업과 터미널 작업
자바 스트림은 지연 평가(lazy evaluation)된다. filter, map 같은 중간 작업은 지정 시 평가되지 않고, 터미널 작업이 호출될 때 계산이 수행된다.
- 중간 작업: Stream을 반환하는 작업 (filter, map 등)
- 터미널 작업: Stream 이외의 것을 반환하는 작업 (forEach, collect, reduce 등)
성능 최적화
중간 작업의 순서가 성능에 영향을 준다. filter()를 map()보다 먼저 지정하면 map()이 적게 호출되어 성능이 향상된다.
키워드
- 순서대로 적용되고 변경할 수 없는 함수들의 추상화
- 데이터가 아닌 기능에 대해 작동
- 주요 작업: filter, map, reduce, collect
- 지연 평가: 터미널 작업 호출 시 실행