Search

Item 46. 스트림에서는 부작용 없는 함수를 사용하라

생성일
2023/07/31 23:51
챕터
7장 - 람다와 스트림

요약

스트림을 잘 활용하고, 스트림에 람다식을 적용할 거면 부작용(side effect)가 없는지 확인해라.

스트림과 순수 함수

스트림의 핵심은 여러 계산들을 일련의 변환(transformation)들로 재구성하는 부분이다. 이러한 각 변환 단계는 이전 단계의 결과를 받아서 처리하고, 오직 입력만이 결과에 영향을 주는 순수 함수이어야 한다.
순수 함수에서는 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다. 이를 위해 스트림에 사용되는 모든 함수 객체는 부작용(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); }); }
Java
복사
위 코드는 freq라는 외부 변수를 스트림의 람다식 내부에서 수정하기 때문에 문제가 된다.
Map<String, Long> freq; try (Stream<String> words = new Scanner(file).tokens()) { freq = words.collect(groupingBy(String::toLowerCase, counting())); }
Java
복사
위의 forEach 연산은 최종 연산 중 가장 기능이 적고 반복이라 병렬화도 할 수 없다. 그렇기 때문에 forEach 연산은 스트림 계산 결과를 추리는데 사용하고, 계산 자체에는 가급적 사용하지 말자.

collector

collector의 toList(), toSet(), toCollection(collectionFactory) 세 가지 메서드를 사용하면 스트림의 원소들을 쉽게 컬렉션으로 변환할 수 있다.
List<String> topTen = freq.keySet().stream() .sorted(comparing(freq::get).reversed()) .limit(10) .collect(Collectors.toList());
Java
복사
Collectors에는 그 외에도 36개의 메서드들이 더 있다. 그 중 대부분은 스트림을 Map으로 취합하는 기능으로, 스트림의 각 원소를 키 하나와 값 하나에 연관시키는 것들이다.
private static final Map<String, Operation> stringToEnum = Stream.of(values()).collect(toMap(Object::toString, e -> e));
Java
복사
이러한 toMap 형태는 스트림의 각 원소가 고유한 키로 매핑되어 있어야 한다. 다수의 원소가 같은 키를 사용한다면 파이프라인이 IllegalStateException을 던지며 종료된다.