5. 스트림 활용

데이터를 어떻게 처리할지는 스트림 API가 관리.

편리하게 데이터 관련 작업 가능.

스트림 API 내부적으로 다양한 최적화가 이루어질 수 있음.

스트림 API는 내부 반복 뿐 아니라 코드를 병렬로 실행할지 여부도 결정 가능.

스트림 API가 지원하는 연산을 이용하여 필터링, 슬라이싱, 매핑, 검색, 매칭, 리듀싱 등 다양한 데이터 처리 질의 표현 가능.


1. 필터링과 슬라이싱

1.1 프레디케이트로 필터링

- filter 메서드는 프레디케이드(boolean을 반환하는 함수)를 인수로 받아 프레디케이트와 일치하는 모든 요소를 포함하는 스트림 반환.


1.2 고유 요소 필터링

- 고유 요소로 이루어진 스트림을 반환하는 distinct 메서드 지원.

> 고유 여부는 스트림에서 만든 객체의 hashCode, equals로 결정됨.


1.3 스트림 축소

- 주어진 사이즈 이하의 크기를 갖는 새로운 스트림을 반환하는 limit 메서드 지원.

- 스트림이 정렬되어 있으면 최대 n개의 요소 반환 가능.


1.4 요소 건너뛰기

- 처음 n개의 요소를 제외한 스트림을 반환하는 skip 메서드 지원.

- limit과 skip은 상호 보완적 연산을 수행.


2. 매핑

- 특정 객체에서 특정 데이터를 선택하는 작업.

- 스트림 API의 map과 flatMap 메서드는 특정 데이터를 선택하는 기능 제공.


2.1 스트림의 각 요소에 함수 적용

- 스트림은 함수를 인수로 받는 map 메서드 지원.

- 인수로 제공된 함수는 각 요소에 적용. 함수를 적용한 결과가 새로운 요소로 매핑.

> 이 과정은 기존의 값을 "고친다 modify"라는 개념 보다 "새로운 버전 생성"이라는 개념에 가까움.

> "변환 transforming"에 가까운 "매핑 mapping"이라는 단어 사용.


2.2 스트림 평면화

- 스트림의 각 값을 다른 스트림으로 만든 다음 모든 스트림을 하나의 스트림으로 연결하는 메서드를 flatMap 메서드라 함.

- flatMap은 객 배열을 스트림이 아니라 스트림의 콘텐츠로 매핑함.

> 즉, map과 달리 faltMap은 하나의 평면화된 스트림을 반환.


3. 검색과 매칭

- 특정 속성이 데이터 집합에 있는지 여부를 검색

- allMatch, anyMatch, noneMatch, findFirst, findAny등 다양한 유틸리티 메서드 제공.


3.1 프레디케이트가 적어도 한 요소와 일치하는지 확인

- 프레디케이트가 주어진 스트림에 적어도 한 요소와 일치하는지 확인할 때 anyMatch 메서드 이용.

- anyMatch는 boolean 반환하는 최종 연산.


3.2 프레디케이트가 모든 요소와 일치하는지 확인

- 프레디케이트가 주어진 스트림의 모든 요소와 일치하는지 확인 할 때 allMatch 메서드 이용.

- 프레디케이드가 주어진 스트림의 일치하는 요소가 없는 확인 할 때 noneMatch 메서드 이용. (allMatch 와 반대 연산)

- anyMatch, allMatch, noneMatch 세 가지 메서드는 스트림 쇼트서킷 기법, 즉 자바의 &&, ||와 같은 연산.


쇼트서킷이란?

- 여러 and 연산으로 연결된 커다란 boolean 표현식을 평가한다고 가정할 때, 표현식에서 하나라도 거짓이라는 결과가 나오면 나머지 표현식의 결과와 상관없이 전체 결과도 거짓이 됨.

> 이러한 상황을 쇼트서킷이라 부름.


3.3 요소 검색

- 현재 스트림에서 임의 요소 반환하는 매서드는 findAny.

- findAny 메서드를 다른 스트렘 연산과 연결해서 사용 가능.


Optional이란?

- 값의 존재나 부재 여부를 표현하는 컨테이너 클래스.

- fnidAny는 아무 요소도 반환하지 않을 수 있음.

- null은 쉽게 에러를 발생 시킬 수 있으므로 자바 8 라이브러리 설계자는 Optional<T> 기능 구현.


1) isPresent() - Optional이 값을 포함하는 true 반환. 포함하지 않으면 false 반환.

2) ifPresnet(Consumer<T> block) - 값이 있으면 주어진 블록 실행. T 형식의 인수를 받으며 void를 반환하는 람다 전달 가능.

3) T get() - 값이 존재하면 값 반환. 값이 없으면 NoSuchElementException 발생.

4) T orElse(T Other) - 값이 있으면 값 반환. 값이 없으면 기본값 반환.


3.4 첫번째 요소 찾기

- 리스트 또는 정렬된 연속 데이터로부터 생성된 스트림처럼 일부 스트림에는 논리적 아이템 순서가 정해져있을 수 있음.

- 이런 스트림의 첫 번재 요소를 찾는 findFirst 메서드 제공.


4. 리듀싱

- 모든 스트림 요소를 처리해 값으로 도출하는 질의를 리듀싱 연산이라 함.

- 함수형 프로그래밍 언어 용어로는 이 과정이 마치 종이를 작은 조각이 될 때까지 반복해서 접는 것과 비슷하다는 의미로 폴드(fold)라 부름.


표 5-1 작성.


5. 실전 예제

- 추가 포스팅에서 예제 제공.


6. 숫자형 스트림

- 숫자 스트림을 효율적으로 처리할 수 있도록 스트림 API는 기본형 특화 스트림 제공.


6.1 기본형 특화 스트림

- 박싱 비용을 피할 수 있도록 'int', 'double', 'long' 요소에 특화된 스트림 제공

> IntStream, DoubleStream, LongStream

- 각각의 인터페이스는 숫자 스트림의 합계를 계산할 수 있는 sum, 최댓값 요소를 검색하는 max등 자주 사용하는 숫자 관련 리듀싱 연산 수행 메서드 제공.

- 필요할 때 다시 객체 스트림으로 복원하는 기능도 제공.

- 특화 스트림은 오직 박싱과정에서 일어나는 효율성과 관련 있으며 스트림에 추가 기능을 제공하지 않음.

- boxed()를 통해 특화 스트림을 일반 스트림으로 변환 가능.

- Optional -> OptionalInt, OptionalDouble, OpionalLong 특화 스트림 버전 제공.


6.2 숫자 범위

- IntStream, LongStream에 range와 rangeClosed 두 가지 정적 메서드 제공.


7. 스트림 만들기

- stream 메서드로 컬렉션에서 스트림 획득 가능.


7.1 값으로 스트림 만들기

- 임의의 수를 인수로 받는 정적 메서드 Stream.of를 이용해 스트림 생성 가능.


7.2 배열로 스트림 만들기

- 배열을 인수로 받는 정적 메서드 Arrays.stream을 이용해 스트림 생성 가능.


7.3 파일로 스트림 만들기

- java.nio.file.Files의 많은 정적 메서드가 스트림을 반환.


7.4 함수로 무한 스트림 만들기

- 함수에서 스트림을 만들 수 있는 두 개의 정적 메서드 Stream.iterate와 Stream.generate 제공.

- iterate와 generate에서 만든 스트림은 요청할 때 마다 주어진 함수를 이용해 값을 생성.

> 따라서 무제한으로 값 계산 가능.

- iterate

> 초깃값과 람다를 신수로 받아 새로운 값을 끊임없이 생산 가능.

> 요청할 떄 마다 값을 생산 할 수 있으며 무한 스트림 생성 가능.

* 이러한 스트림을 언바운드 스트림이라 표현.

> 일반적으로 연속된 일련의 값을 만들때 iterate 사용.

- generate

> 요구할 떄 값을 계산하는 무한 스트림 생성 가능.

> iterate와 달리 생산된 각 값을 연속적으로 계산하지 않음.

> Supplier<T>를 인수로 받아 새로운 값을 생산.


8. 요약

- 복잡한 데이터 처리 질의 표현 가능.

- filter, distinct, skip, limit 메서드로 스트림을 필터링 하거나 자르기 가능.

- map, flatMap 메서드로 스트림 요소를 추출하거나 변환 가능.

- findFirst, findAny 메서드로 스트림의 요소 검색 가능.

- allMatch, noneMatcj, anyMatch 메서드를 이용해서 주어진 프레디케이트와 일치하는 요소를 스트림에서 검색 가능.

> 이들 메서드를 쇼트서킷, 즉 결과를 찾는 즉시 반환, 전체 스트림을 처리하지 않음.

- reduce 메서드로 스트림의 모든 요소를 반복 조합하며 값 도출 가능.

- filter, map등은 상태를 지정하지 않는 상태없는 연산.

- reduce 같은 연산은 계산하는데 필요한 상태 저장.

- sorted, distinct 등의 메서드는 새로운 스트림을 반환하기에 앞서 스트림의 모든 요소를 버퍼에 저장.

> 이런 메서드를 상태 있는 연산이라 부름.

- IntStream, DoubleStram, LongStream은 기본형 특화 스트림.

> 이들 연산은 기본형에 맞게 특화.

- 컬렉션뿐 아니라 값, 배열, 파일, iterate, generate 같은 메서드로도 스트림 생성 가능.

- 크기가 정해지지 않은 스트림을 무한 스트림이라 함.

'Java8 > Java 8 in Action' 카테고리의 다른 글

스트림으로 데이터 수집  (0) 2017.06.29
스트림 활용 예제 소스  (0) 2017.06.26
스트림 소개  (0) 2017.06.16
람다 표현식  (0) 2017.06.15
동작 파라미터화 예제 소스  (0) 2017.06.13

+ Recent posts