스트림이란?
- 스트림 API는 다량의 데이터 처리 작업(순차적이든 병렬적이든)을 돕고자 자바 8에 추가되었다.
- 스트림 API가 제공하는 추상 개념 중 핵심은 두 가지다.
- 첫 번째, 스트림(stream)은 데이터 원소의 유한 혹은 무한 시퀀스(sequence)를 뜻한다.
- 두 번째, 스트림 파이프라인(stream pipeline)은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다.
스트림 안의 데이터 원소들은 객체 참조나 기본 타입 값이다. 기본 타입 값으로는 int, long, double 이렇게 세 가지를 지원한다.
- 스트림 API는 메서드 연쇄를 지원하는 플루언트 API(fluent API)다.
스트림 파이프라인
- 스트림 파이프라인은 소스 스트림에서 시작해 종단 연산(terminal operation)으로 끝나며, 그 사이에 하나 이상의 중간 연산(intermediate operation)이 있을 수 있다.
- 각 중간 연산은 스트림은 어떠한 방식으로 변환(transform)한다.
예컨대 각 원소에 함수를 적용하거나 특정 조건을 만족 못하는 원소를 걸러낼 수 있다. - 중간 연산들은 모두 한 스트림을 다른 스트림으로 변환하는데, 변환된 스트림의 원소 타입은 변환 전 스트림의 원소 타입과 같은 수도 있고 다를 수도 있다.
- 종단 연산은 마지막 중간 연산이 내놓은 스트림에 최후의 연산을 가한다.
원소를 정렬해 컬렉션에 담거나, 특정 원소 하나를 선택하거나, 모든 원소를 출력하는 식이다.
- 각 중간 연산은 스트림은 어떠한 방식으로 변환(transform)한다.
- 스트림 파이프라인은 지연 평가(lazy evaluation)된다.
- 평가는 종단 연산이 호출될 때 이뤄지며, 종단 연산에 쓰이지 않는 데이터 원소는 계산에 쓰이지 않는다.
- 이러한 지연 평가가 무한 스트림을 다룰 수 있게 해주는 열쇠다.
- 종단 연산이 없는 스트림 파이프라인은 아무 일도 하지 않는 명령어인 no-op과 같으니, 종단 연산을 빼먹는 일이 없도록 하자.
- 기본적으로 스트림 파이프라인은 순차적으로 수행된다.
- 파이프라인을 병렬로 실행하려면 파이프라인을 구성하는 스트림 중 하나에서 parallel 메서드를 호출해주기만 하면 되나, 효과를 볼 수 있는 상황은 많지 않다. (아이템48)
[Why]
- 스트림을 과용하면 프로그램이 읽거나 유지보수하기 어려워진다.
"Hello world!".chars().forEach(System.out::print);
- Hello world!를 출력하리라 기대했겠지만, 72101108108111321191111141810033을 출력한다.
- "Hello world!".chars()가 반환하는 스트림의 원소는 char가 아닌 int 값이기 때문이다.
- 올바른 print 메서드를 호출하ㄹ게 하려면 형변환을 명시적으로 해야한다.
- char 값들을 처리할 때는 스트림을 삼가는 편이 낫다.
[When]
- 기존 코드는 스트림을 사용하도록 리팩터링하되, 새 코드가 더 나아 보일 때만 반영하자.
스트림을 사용하지 않는 경우
- 코드 블록에서는 범위 안의 지역변수를 읽고 수정할 수 있다.
람다에서는 final이거나 사실상 final인 변수만 읽을 수 있고, 지역변수를 수정하는 건 불가능하다. - 코드 블록에서는 return문을 사용해 메서드에서 빠져나가거나, break이나 continue문으로 블록 바깥의 반복문을 종료하거나 반복을 한 번 건너뛸 수 있다.
- 코드 블록에서는 메서드 선언에 명시된 검사 예외를 던질 수 있다.
- 파이프라인의 여러 단계(stage)를 통과할 때 이 데이터의 각 단계에서의 값들에 동시에 접근하기는 어려운 경우
스트림 파이프라인은 일단 한 값을 다른 값에 매핑하고 나면 원래의 값은 잃는 구조이기 때문이다.
스트림을 사용하는 경우
- 원소들의 시퀀스를 일관되게 변환한다.
- 원소들의 시퀀스를 필터링한다.
- 원소들의 시퀀스를 하나의 연산을 사용해 결합한다. (더하기, 연결하기, 최솟값 구하기 등)
- 원소들의 시퀀스를 컬렉션에 모은다.
- 원소들의 시퀀스에서 특정 조건을 만족하는 원소를 찾는다.
[How]
- 람다 매개변수의 이름은 주의해서 정해야 한다. 람다에서는 타입 이름을 자주 생략하므로 매개변수 이름을 잘 지어야 스트림 파이프라인의 가독성이 유지된다.
- 도우미 메서드를 적절히 활용하는 일의 중요성은 일반 반복 코드에서보다는 스트림 파이프라인에서 훨씬 크다.
'독서찰기(讀書札記) > 이펙티브 자바' 카테고리의 다른 글
[아이템 47] 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) | 2022.03.09 |
---|---|
[아이템 46] 스트림에서는 부작용 없는 함수를 사용하라 (0) | 2022.03.05 |
[아이템 44] 표준 함수형 인터페이스를 사용하라 (0) | 2022.03.04 |
[아이템 43] 람다보다는 메서드 참조를 사용하라 (0) | 2022.03.03 |
[아이템 42] 익명 클래스보다는 람다를 사용하라 (0) | 2022.03.02 |