요약
•
스트림을 잘 활용하고, 스트림에 람다식을 적용할 거면 부작용(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을 던지며 종료된다.