Item 46. Side-Effect-Free Function

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

스트림은 함수형 프로그래밍에 기초한 패러다임이기 때문에, 스트림에도 함수형 프로그래밍의 패러다임이 적용된다. 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분인데, 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다.

// 안 좋은 예시
class Example {
    public static void main(String[] args) {
        Map<String, Long> freq = new HashMap<>();
        try (Stream<String> words = new Scanner(file).tokens()) {
            // 스트림 API를 사용했지만 단순 반복문을 사용한 것과 같다.
            words.forEach(word -> {
                // 단어를 소문자로 변환한 다음, 외부 상태를 수정(연산 결과를 보여주는 일 이상의 일을 수행 중)
                freq.merge(word.toLowerCase(), 1L, Long::sum);
            });
        }
    }
}

// 좋은 예시
class Example {
    public static void main(String[] args) {
        Map<String, Long> freq;
        try (Stream<String> words = new Scanner(file).tokens()) {
            // 스트림 파이프라인을 이용해 외부 상태를 수정하지 않고 단어 빈도를 계산
            freq = words
                .collect(groupingBy(String::toLowerCase, counting()));
        }
    }
}

forEach 연산은 종단 연산 중 기능이 가장 적고 스트림 답지 못한 연산이기 때문에, 위와 같이 스트림 패러다임의 장점을 제대로 살리지 못할 수 있다. 때문에 forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고, 계산하는 데는 사용하지 않는 것이 좋다.

Collectors

Collectors는 스트림의 원소를 손쉽게 컬렉션으로 모을 수 있게 해주는데, 스트림을 올바르게 활용하려면 Collectors를 잘 알아두는 것이 좋다. toList, toSet, toCollection, toMap, groupingBy, partitioningBy 등 다양한 메서드를 제공하는데, 책의 본문을 참고하면 된다.

Last updated

Was this helpful?