아이템 46. 스트림에서는 부작용 없는 함수를 사용하라
- 스트림은 그저 또 하나의 API가 아닌, 함수형 프로그래밍에 기초한 패러다임임
- 스트림이 제공하는 표현력, 속도, (상황에 따라서는) 병렬성을 얻으려면 API와 이 패러다임까지 함께 받아드려야 함
- 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분
- 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야함
순수함수
- 오직 입력만이 결과에 영향을 주는 함수를 말함
- 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는함.
- 이렇게 하려면(중간 단계든 종단 단계든) 스트림 연산에 건네는 함수 객체는 모두 부작용이 없어야함
스트림패러다임을 이해하지 못한 코드
Map<String ,Long> freq = new HashMap<>();
try(Stream<String> words = new Scanner(file).tokens()) {
words.forEach(word ->{
freq.merge(word,1L,Long::sum);
});
}
- 스트림 API의 이점을 살리지 못함
- 해당 코드는 forEach 에서 모든 작업이 일어나는데 이때 외부 상태를 수정하는 람다를 실행하면서 문제가 생김
- forEach 연산은 종단 연산 중 기능이 가장 적고 가장 '덜' 스트림함. 대놓고 반복적이라서 병렬화할 수도 없음
- forEach연산은 스트림 계산 결과를 보고할 때만 사용하고, 계산하는 데는 쓰지 말 것
개선된 버전
Map<String ,Long> freq;
try(Stream<String> words = new Scanner(file).tokens()) {
freq=words.collect(Collectors.groupingBy(String::toLowerCase,counting()));
}