스트림이란?
데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소
스트림은 함수형 프로그래밍 언어와 데이터베이스에서 일반적으로 지원하는 연산을 지원한다. 예를 들어 filter, map, find, match, sort 등으로 데이터를 조작할 수 있다. 스트림 연산은 순차적으로 또는 병렬로 실행할 수 있다.
예시를 보자.
menu.stream()
.filter(dish -> dish.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(toList());
1. 데이터 소스 menu는 연속된 요소를 스트림에 제공한다.
2. stream()을 통해 스트림을 얻고
3. 데이터 처리 연산(filter, map, limit, collect)을 통해 계산하고 파이프라인을 형성할 수 있도록 스트림을 반환한다.
4. 마지막으로 collect 연산은 파이프라인을 처리하여 List로 결과를 반환한다.
filter, map, limit, collect는 다음 작업을 수행한다.
- filter : 람다를 인수로 받아 스트림에서 특정 요소를 필터링한다. 예제에서는 300칼로리 이상의 요리를 선택한다.
- map : 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출한다. 예제에서는 각 요리명을 추출한다.
- limit : 정해진 개수 이상의 요소가 스트림에 저장되지 못하게 스트림 크기를 축소 truncate한다.
- collect : 스트림을 다른 형식으로 변환한다. 예제에서는 스트림을 리스트로 변환했다.
중간 연산
filter, sorted, map과 같은 스트림 연산을 중간 연산이라고 하고, 스트림을 닫는 연산을 최종 연산이라고 한다. 중간 연산의 특징은 중간 연산을 합친 다음에 중간 연산을 최종 연산으로 한번에 처리하는 Lazy라는 것이다. 예시를 보겠다.
List<String> names =
menu.stream()
.filter(dish -> {
System.out.println("filtering:" + dish.getName());
return dish.getCalories() > 300;
})
.map(dish -> {
System.out.println("mapping:" + dish.getName());
return dish.getName();
})
.limit(3)
.collect(toList());
System.out.println(names);
실행결과는 다음과 같다.
filtering:pork
mapping:pork
filtering:beef
mapping:beef
filtering:chicken
mapping:chicken
[pork, beef, chicken]
연산 | 형식 | 반환 형식 | 연산의 인수 | 함수 디스크립터 |
filter | 중간 연산 | Stream<T> | Predicate<T> | T -> boolean |
map | 중간 연산 | Stream<R> | Function<T,R> | T -> R |
limit | 중간 연산 | Stream<T> | ||
sorted | 중간 연산 | Stream<T> | Comparator<T> | (T, T) -> int |
distinct | 중간 연산 | Stream<T> |
최종 연산
스트림 파이프라인에서 결과를 도출하는 마지막 단계다. 보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환된다.
연산 | 형식 | 반환 형식 | 목적 |
forEach | 최종 연산 | void | 스트림의 각 요소를 소비하면서 람다를 적용한다. |
count | 최종 연산 | long(generic) | 스트림의 요소 개수를 반환한다. |
collect | 최종 연산 | 스트림을 reduce해서 List, Map, Integer 형식의 컬렉션을 만든다. |
반복자와 마찬가지로 스트림도 한번만 탐색할 수 있다. 탐색된 스트림의 요소는 소비된다. 반복자와 마찬가지로 한번 탐색한 요소를 다시 탐색하려면 초기 데이터 소스(컬렉션처럼 반복 사용할 수 있는 데이터 소스)에서 새로운 스트림을 만들어야 한다.
스트림과 컬렉션의 차이
스트림은 컬렉션과 다른 부분을 담당하고 있다. 컬렉션은 자료구조이므로 시간과 공간 복잡성과 관련한 요소 저장 및 접근 연산이 주를 이룬다. 반면 스트림은 filter, sorted, map처럼 표현 계산식이 주를 이룬다. 즉, 컬렉션의 주제는 데이터고 스트림의 주제는 계산이다.
또한 데이터를 언제 계산하느냐가 큰 차이점이다. 컬렉션은 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조다. 즉, 컬렉션의 모든 요소는 컬렉션에 추가하기 전에 계산되어야 한다. 반면 스트림은 요청할 때만 요소를 계산하는 고정된 자료구조다. JPA에서 데이터를 조회할 때 lazy(지연로딩)와 eager(즉시로딩)의 차이와 비슷하다.
'Java' 카테고리의 다른 글
스트림(2) - Stream API의 다양한 연산 (0) | 2024.10.26 |
---|---|
equals와 hashCode (0) | 2024.10.25 |
람다, 메소드 참조 활용하기 (1) | 2024.10.21 |
메소드 참조 (1) | 2024.10.21 |
람다 표현식 (1) | 2024.10.21 |