•
컬렉션 스트림부터 살펴보자.
•
자바 8 컬렉션에 스트림으로 변환하는 stream 메서드가 추가됐다.
•
숫자 범위나 I/O 자원에서 스트림 요소를 만드는 등 stream 메서드 이외에도 다양한 방법으로 스트림을 얻을 수 있다.
•
스트림이란 정확히 무엇일까? 스트림이란 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소로 정의할 수 있다.
◦
연속된 요소
▪
스트림은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스를 제공한다.
▪
컬렉션에서는 시간과 공간의 복잡성에 관련된 요소 저장 및 접근 연산이 주인 반면 스트림은 filter, sorted, map처럼 표현 계산식이 주를 이룬다.
▪
즉 컬렉션의 주제는 저장, 조회고 스트림의 주제는 연산이다.
◦
소스
▪
스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터를 소비한다.
▪
정렬된 컬렉션으로 스트림을 생성하면 정렬이 그대로 유지된다.
▪
즉 리스트로 스트림을 만들면 스트림의 요소는 리스트의 요소와 같은 순서를 유지한다.
◦
데이터 처리 연산
▪
스트림은 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원한다.
▪
예를 들어 filter, map, reduce, find, match, sort 등으로 데이터를 조작할 수 있다.
▪
또한 스트림 연산은 순차적 또는 병렬로 실행할 수 있다.
•
이제 스트림의 중요한 2가지 특징을 살펴보자.
◦
파이프라이닝
▪
대부분의 스트림 연산은 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있도록 스트림 자신을 반환한다.
▪
그 덕분에 우리는 lazy-evaluation, short-circuit같은 최적화도 얻을 수 있다. 연산 파이프라인은 데이터 소스에 적용하는 쿼리와 유사하다.
•
lazy evaluation: 해당 계산식의 실제 수행을 실행 결과가 필요할 때까지 늦추는 것.
•
short-circuiting: 논리 연산에서 두 피연산자 중 어느 한 쪽만 '참'이면 우측 피연산자의 값은 평가하지 않고 바로 결과값을 얻는 것.
◦
내부 반복
▪
반복자를 이용해서 명시적으로 반복하는 컬렉션과는 달리 스트림은 내부 반복을 지원한다.
•
지금까지 설명한 내용을 예제로 확인해보자.
List<String> threeHighCaloricDishNames =
menu.stream() // 메뉴에서 스트림을 얻는다.
// 파이프라인 연산 만들기
.filter(dish -> dish.getCalories() > 300) // 고칼로리 요리 필터링, Stream<Dish> // 중간 연산
.map(Dish::getName) // 요리명 추출 Stream<String> // 중간 연산
.limit(3) // 선착순 3개만 선택 Stream<String> // 중간 연산
.collect(toList()); // 결과를 다른 리스트로 저장 List<String>
Java
복사
◦
우선 요리 리스트인 menu에 stream 메서드를 호출해서 스트림을 얻었다. 여기서 데이터 소스는 요리 리스트다.
◦
데이터 소스는 연속된 요소를 스트림에 제공한다.
◦
다음으로 스트림에 filter, map, limit, collect로 이어지는 일련의 데이터 처리 연산을 적용한다.
◦
collect를 제외한 모든 연산은 서로 파이프라인을 형성할 수 있도록 스트림을 반환한다. 파이프라인은 소스에 적용하는 SQL 질의같은 느낌의 존재다.
◦
마지막으로 collect 연산으로 파이프라인을 처리해서 결과를 반환한다.(collect는 stream이 아닌 List를 반환)
◦
마지막에 collect를 호출하기 전까지 menu에서 무엇도 선택되지 않으며 출력 결과도 존재하지 않는다. 즉 collect가 호출되기 전까지 메서드 호출이 저장되는 효과가 있다.