- 스트림이란 무엇인가?
- 컬렉션과 스트림
- 내부 반복과 외부 반복
- 중간 연산과 최종 연산
스트림은 자바 API에 새로 추가된 기능으로,
- 선언형(임시구현 대신 질의로 표현)으로 컬렉션 데이터를 처리할 수 있다.
- 멀티 스레드 코드를 구현하지 않아도 데이터를 투명하게 병렬로 처리할 수 있다.
- 기존 코드 : p128 참고
- 최신 코드
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName =
menu.stream() //병렬 실행 시 .parallelStream()으로 교체
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getcalories))
.map(Dish::getName)
.collet(toList());: for 루프와 if 조건문으로 어떻게 동작을 구현할지 지정하는 대신 getCalories() 라는 동작의 수행을 지정한다.
: filter, sorted, map, collect와 같은 여러 빌딩 블록 연산을 연결해 복잡한 데이터 처리 파이프라인을 만들 수 있다.
= 가독성과 명확성이 유지된다.
- 자바 8 스트림 API의 특징 요약
- 선언형: 간결하고 가독성이 좋아진다.
- 조립할 수 있음: 유연성이 좋아진다.
- 병렬화: 성능이 좋아진다.
스트림이란?
= 데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소
-
연속된 요소: 컬렉션과 마차가지로 특정 요소 형식으로 이루어진 연속된 값 집합의 인터페이스 제공. 컬렉션에서의 시공간의 복잡성과 관련된 요소 저장 및 접근 연산과 달리 스트림은 filter, sorted, map 과 같은 표현 계산식이 주를 이룬다.
-
소스: 스트림은 컬렉션, 배열, I/O 자원 등의 소스로부터 데이터를 소비한다.(컬렉션이나 리스트로 스트림을 만들면 정렬도 유지))
-
데이터 처리 연산: 함수형 프로그래밍 언어에서 일반적으로 지원하는 연산과 DB와 비슷한 연산을 지원한다.(filter, map, reduce 등)
-
파이프라이닝: 스트림 연산끼리 연결해서 커다란 파이프라인을 구성할 수 있다. 게으름, 쇼트서킷 같은 최적화를 얻을 수 있다.
-
내부 반복: 데이터 소스에서 연속된 요소를 스트림에 제공하고 나면 이어지는 일련의 데이터 처리 연산을 적용한다. 모든 연산은 스트림을 반환하여 이어지다가 collect 연산에서만 결과를 반환한다.
-
filter: 람다를 인수로 받아 스트림에서 특정 요소를 제외시킨다.
-
map: 람다를 이용해서 한 요소를 다른 요소로 변환하거나 정보를 추출한다.
-
limit: 정해진 개수 이상의 요소가 스트림에 저장되지 못하게 스트림 크기를 축소한다.
-
collect: 스트림을 다른 형식으로 변환한다.
자바는 컬렉션과 스트림 모두 연속된 요소 형식의 값을 저장하는 자료구조의 인터페이스를 제공한다.
연속된 요소 형식 : 순차적으로 값에 접근한다는 것
-
스트림과 컬렉션의 차이 = 데이터를 언제 계산하느냐!
- 컬렉션
: 현재 자료구조가 포함하는 모든 값을 메모리에 저장하는 자료구조.
모든 요소는 추가하기 전에 계산된다.
생성자 중심으로 적극적으로 생성된다. - 스트림
: 이론적으로 요청할 때만 요소를 계산하는 고정된 자료구조.
스트림에 요소를 추가하거나 제거할 수 없다.
생산자와 소비자(Producer-Consumer) 관계를 형성한다. 게으르게 만들어지는 컬렉션과 같다.
- 컬렉션
-
스트림은 딱 한번만 탐색할 수 있다.
- 탐색된 스트림의 요소는 소비된다. 다시 탐색하려면 다시 스트림을 만들어야 한다.
- 컬렉션처럼 반복 사용 가능한 데이터 소스가 아니면(Ex. IO 채널) 소스를 반복 사용할 수 없어 새로운 스트림을 만들 수 없다.
-
외부반복과 내부반복
- 외부반복(Collection): 사용자가 직접 요소를 반복한다. 사용자가 병렬성을 직접 관리한다.
- 내부반복(Stream): 어떤 일을 수행할지만 지정하면 반복을 알아서 처리하고 결과를 어딘가에 처리해준다. 작업을 투명하게, 병렬로, 최적화된 순서로 처리한다.
- 중간 연산: 다른 스트림을 반환한다. 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다.
- 최종 연산: 스트림 파이프라인에서 결과를 도출한다.
- 스트림 이용과정
- 질의를 수행할 (컬렉션과 같은) 데이터 소스
- 스트림 파이프라인을 구성할 중간 연산 연결
- 스트림 파이프라인을 실행하고 결과를 만들 최종 연산
** 빌더패턴
** Quiz
Q. 문제점을 찾아라
public static void main(String[] args) {
List<String> myLocalList = new ArrayList<>();
IntStream.rangeClosed(1, 10000)
.parallel
.forEach(i -> {
myLocalList.add(String.valueOf(i));
});
}A. ArrayList의 경우 Thread-safe 하지 않으므로 병렬(parallel)로 실행하면 일정한 값이 도출되지 않는다.
예제의 경우 myLocalList.size()는 10000일수도 있고 아닐 수도 있다.
