참조
핵심
NOTE
•
스트림이란 무엇인가?
•
컬렉션과 스트림
•
내부 반복과 외부 반복
•
중간 연산과 최종 연산
스트림이란 무엇인가?
NOTE
스트림 ⇒ 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소로 정의할 수 있다!
DB SQL로 처리하는게 좋을까, Stream으로 처리하는게 좋을까..
•
연속된 요소
◦
스트림은 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스
•
소스
◦
스트림은 컬렉션, 배열, I/O 자원 등의 데이터 제공 소스로부터 데이터 소비한다.
•
데이터 처리 연산
◦
함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 데이터베이스와 비슷한 연산을 지원, 순차-병렬 실행가능
스트림 등장배경
NOTE
효율적인 데이터 처리, 함수형 프로그래밍 지원, 멀티코어 활용을 위해 도입되었다!
•
SQL 질의 언어에서는 기대하는 필터링을 어떻게 구현해야 할지 명시할 필요가 없으며, 자동으로 제공한다.
•
컬렉션으로도 이와 비슷한 기능(필터링 자동제공)을 만들거나, 멀티코어 아키텍쳐에서 컬렉션을 병렬처리를 통해 성능을 높이는 방법을 제공할 수 없는가?
스트림 특징
NOTE
import static java.util.Stream.Collectors.toList;
List<String> threeHighCaloricDishNames =
menu.stream() // menu(요리 리스트)에서 stream() 메서드를 호출해서 스트림을 얻었다.
.filter(dish -> dish.getCalories() > 300) // 고칼로리 요리를 필터링
.map(Dish::getName) // 요리명 추출
.limit(3) // 선착순 3개만 선택
.collect(toList()); // 결과를 리스트로 저장
Java
복사
예시 스트림 코드
•
파이프 라이닝
◦
대부분의 스트림 연산은 연산끼리 연결해서 거대한 파이프라이닝을 구성할 수 있도록, 스트림 자신을 반환한다.
◦
덕분에 laziness(게으른 연산), short-circuiting(이미 결정난 값에 불필요한 연산 하지 않음) 같은 최적화를 얻을 수 있다.
•
내부 반복
◦
반복자를 이용해서 명시적으로 반복하는 컬렉션과 달리 내부 반복을 지원한다!
위의 특징을 통해 아래의 장점들을 얻을 수 있다!
•
선언형(코드 가독성)
◦
스트림을 이용하면 선언형(데이터를 처리하는 임시 구현 코드 대신 질의로 표현)으로 컬렉션 데이터를 처리할 수 있다.
•
조립할 수 있음(코드 유연성 증가)
◦
filter, sorted, map, collect와 같은 여러 빌딩 블록 연산을 연결해서 복잡한 데이터 처리 파이프라인을 만들 수 있다.
•
병렬형(성능이 좋아짐)
◦
멀티스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리할 수 있다.
◦
고수준 빌딩 블록으로 이뤄져 있으므로 특정 스레딩 모델에 제한되지 않고 자유롭게 어떤 상황에서든 이용할 수 있다.
◦
내부적으로 단일 스레드 모델에 사용할 수 있지만, 아키텍쳐를 최대한 투명하게 활용하도록 구현되어 있다.
▪
결과적으로 데이터 처리과정을 병렬화하면서 스레드와 락을 걱정할 필요가 없어짐
스트림과 컬렉션
NOTE
자바의 기존 컬렉션과 새로운 스트림 모두 연속된 요소(순차적 값 접근) 형식의 값을 저장하는 자료구조의 인터페이스를 제공한다!
컬렉션(전체 데이터) → 모두 다운받아야 실행
스트림(필요한 데이터만) → 필요한 (현재 보는 시점의 +10초?) 정도 데이터만
•
스트림과 컬렉션은 어떠한 차이가 있는가?
◦
데이터를 계산하는 시점
◦
반복사용 여부
◦
외부 반복과 내부 반복
데이터를 언제 계산하는가? (전체 vs 필요)
NOTE
컬렉션과 스트림의 가장 큰 차이점은 데이터를 언제 계산하느냐이다!
•
컬렉션
◦
현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조
◦
컬렉션의 모든 요소는 추가/삭제 되기전에 미리 계산되어야 한다.
•
스트림
◦
이론적으로 요청할 때만 요소를 계산하는 고정된 자료구조
◦
스트림에서는 요소 추가/삭제 불가능
◦
요청하는 값만 스트림에서 추출
스트림은 딱 한번만 탐색할 수 있다. (일회용)
NOTE
스트림은 한 번만 탐색할 수 있다.
이미 forEach로 소비된 Stream은 다시 사용하지 못한다.
•
즉, 탐색된 스트림의 요소는 소비된다.
◦
다시 탐색 하려면 초기 데이터 소스에서 새로운 스트림을 만들어야 한다.
그냥 읽으면 좋은거 같아서 들고왔다..
내부 반복과 외부반복
NOTE
내부 반복 vs 외부 반복
•
외부 반복(컬렉션)
◦
사용자가 직접 요소를 반복해야 한다(ex for-each)
◦
병렬성을 스스로 관리해야 한다.
•
내부 반복(스트림)
◦
반복을 알아서 처리하고, 결과 스트림값을 자동으로 저장해준다.
◦
투명하게 병렬로 처리하거나 더 최적화된 다양한 순서로 처리할 수 있다.
스트림 연산
NOTE
스트림 인터페이스의 연산은 크게 2가지로 구분할 수 있다!
중간 연산과 최종 연산
수많은 연산의 종류들이 보이시나요?
중간 연산
NOTE
중간 연산 ⇒ 다른 스트림을 반환한다!(계속 연결가능!)
이미지의 filter, map, limit처럼 계속 연결할 수 있음!
Eager Evaluation
Lazy Evaluation
•
레이지 특성
◦
단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다.
◦
합쳐진 중간 연산을 최종 연산으로 한번에 처리하기 때문
•
이런 레이지 특성 덕문에 몇 가지 최적화 효과를 얻었다.
◦
limit 연산과 쇼트서킷 기법
▪
모든 연산을 다 해보기전에 조건을 만족하면 추가적인 연산을 하지 않는다.
◦
루프 퓨전
▪
둘 이상의 연산이 합쳐 하나의 연산으로 처리된다.
최종 연산
NOTE
최종 연산 ⇒ 스트림을 닫고 파이프라인에서 결과를 도출한다!
대표적인 최종 연
•
보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환된다.