스트림 파이프라인 프로그래밍의 핵심은 부작용 없는 함수 객체에 있다.
스트림 뿐 아니라 스트림 관련 객체에 건네지는 모든 함수 객체가 부작용이 없어야 한다.
스트림을 올바로 사용하려면 수집기를 잘 알아둬야 한다. 가장 중요한 수집기 팩터리는
toList, toSet, toMap, groupingBy, joining 이다.
스트림 패러다임
- 스트림은 그저 또 하나의 API가 아닌, 함수형 프로그래밍에 기초한 패러다임
- 스트림이 제공하는 표현력, 속도, (상황에 따라서는) 병렬성을 얻으려면 API는 말할 것도 없고 이 패러다임까지 함께 받아들여야 한다.
- 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분
- 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 함. (순수 함수란 오직 입력만이 결과에 영향을 주는 함수. 다른 가변 상태를 참조하지 않고 함수 스스로도 다른 상태를 변경하지 않는다.)
- 이렇게 하려면 스트림 연산에 건네는 함수 객체는 모두 부작용이 없어야 함
- 위의 코드 예시는 스트림 코드를 가장한 반복적 코드임. 코드의 모든 작업이 종단 연산인 forEach 에서 일어나게 되고 이때 외부 상태를 수정하는 람다를 실행하면서 문제가 생김
- forEach 연산은 종단 연산 중 기능이 가장 적고 가장 ‘덜’ 스트림다움. 대놓고 반복적이라 병렬화 할 수도 없음. forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고, 계산하는 데는 쓰지 말자.
Collectors(java.util.stream.Collectors)
- 스트림을 사용하려면 꼭 배워야 하는 새로운 개념
- 축소(reduction; 스트림의 원소들을 객체 하나에 취합한다는 의미) 전략을 캡슐화한 블랙박스 객체라고 생각하면 됨
수집기 메서드들
toList(), toSet(), toCollection(collectionFactory)
- toMap() — 맵 수집기
- groupingBy — 입력으로 분류 함수를 받고 출력으로는 원소들을 카테고리별로 모아 놓은 맵을 담은 수집기를 반환
- partitioningBy
- minBy
- maxBy
- joining