아이템 46. 스트림에서는 부작용 없는 함수를 사용하라. - ksw6169/effective-java GitHub Wiki
- 스트림은 계산을 일련의 변환으로 처리한다.
- 이 때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다.
- 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다.
- 순수 함수는 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다.
- 이렇게 하려면 스트림 연산에 건네는 함수 객체는 모두 부작용(side effect)이 없어야 한다.
다음은 텍스트 파일에서 단어별 수를 세어 빈도표를 만드는 코드다.
Map<String, Long> freq = new HashMap<>();
try (Stream<String> words = new Scanner(file).tokens()) {
words.forEach(word -> {
freq.merge(word.toLowerCase(), 1L, Long::sum);
});
}
위 코드는 스트림 코드를 가장한 반복적 코드로 스트림 API의 이점을 살리지 못해 같은 기능의 반복적 코드보다 길고, 읽기 어렵고, 유지보수에도 좋지 않다. 이 코드의 모든 연산이 종단 연산인 forEach 에서 일어나는데, 이때 외부 상태(빈도표)를 수정하는 람다를 실행하면서 문제가 생긴다. 이 코드를 올바르게 작성하면 다음과 같다.
Map<String, Long> freq;
try (Stream<String> words = new Scanner(file).tokens()) {
freq = words.collect(Collectors.groupingBy(String::toLowerCase, counting()));
}
- forEach 연산은 종단 연산 중 기능이 가장 적고 가장 '덜' 스트림답다.
- 대놓고 반복적이라서 병렬화할 수도 없다.
- 따라서 forEach 연산은 스트림 계산 결과를 출력할 때만 사용하고 계산하는 데는 쓰지 말자.
- Effective Java 3/E