[교재 EffectiveJava] 아이템 46. 스트림에서는 부작용 없는 함수를 사용하라

반응형
728x90
반응형

스트림 패러다임

스트림은 그저 또 하나의 API 가 아닌, 함수형 프로그래밍에 기초한 패러다임이다. 스트림이 제공하는 표현력, 속도, 병렬성을 얻으려면 API는 말할것도 없고 이 패러다임까지 함께 받아들여야 한다. 스트림 패러다임의 핵심은 일련의 변환 (transformation)으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다.

* 순수함수
오직 입력만이 결과에 영향을 주는 함수로, 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다.

스트림에서는 순수함수여야 하기 때문에 스트림 연산에 건네는 함수 객체에 모두 부작용이 없어야한다. 

 

 

예제

텍스트 파일에서 단어별 수를 세어 빈도표를 만드는 예제

 

스트림 패러다임을 이해하지 못한채 API만 사용한 잘못된 코드
package com.java.effective.item46;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("/test.txt");

        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 에서 일어나는데, 이때 외부 상태(빈도표)를 수정하는 람다를 실행하면서 문제가 생긴다. 

 

forEach 연산은 종단 연산 중 기능이 가장 적고 가장 덜 스트림스럽다. 병렬화도 할 수 없다. forEach 연산은 스트림 계산 결과를 보고할때만 사용하고, 계산하는데는 쓰지 말자. 

 

스트림을 제대로 활용한 코드
package com.java.effective.item46;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Stream;

import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;

public class MainStream {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("/test.txt");

        Map<String, Long> freq;
        try (Stream<String> words = new Scanner(file).tokens()) {
            freq = words
                    .collect(groupingBy(String::toLowerCase, counting()));
        }
    }
}

java.util.stream.Collectors 클래스는 메서드를 무려 43개 이상을 가지고있고, 그 중에는 타입 매개변수가 5개나 되는것도 있다. 스트림의 원소들을 객체 하나에 취합할 수 있다. 

 

 

 

반응형

Designed by JB FACTORY