NMP-Study / EffectiveJava2018

Effective Java Study
9 stars 0 forks source link

아이템 46. 스트림에서는 부작용 없는 함수를 사용하라 #46

Closed madplay closed 5 years ago

fairsun commented 5 years ago

스트림은 또 하나의 API가 아닌, 함수형 프로그래밍에 기초한 패러다임 스트림이 제공하는 표현력, 속도, (상황에 따라서는) 병령성을 얻으려면 API와 패러다임을 함께 이해 필요 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성.

스트림 패러다임을 이해하지 못한 채 API만 사용한 경우

        Map<String, Long> freq = new HashMap<>();

        List<String> wordList = new ArrayList<>();
        wordList.add("apple");
        wordList.add("lemon");
        wordList.add("banana");

        try (Stream<String> words = wordList.stream()) {
            words.forEach(word -> {
                freq.merge(word.toLowerCase(), 1L, Long::sum);
            });
        }

스트림을 제대로 활용해 빈도표를 초기화한 경우

        Map<String, Long> freq;

        List<String> wordList = new ArrayList<>();
        wordList.add("apple");
        wordList.add("lemon");
        wordList.add("banana");

        try (Stream<String> words = wordList.stream()) {
            freq = words.collect(groupingBy(String::toLowerCase, counting()));
        }

        freq.forEach((k,v)->System.out.println("key : " + k + " Count : " + v));

collector(수집기가 생성하는 객체)

빈도표에서 상위 2개를 뽑아내는 파이프라인

        List<String> topTwo = freq.keySet().stream()
                .sorted(comparing(freq::get).reversed())
                .limit(2)
                .collect(toList());

        topTwo.stream().forEach(v -> System.out.println(v));

toMap

예시1. 수집기를 사용하여 문자열을 열거 타입 상수에 매핑

import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toMap;

public enum Operation {
    PLUS("+") {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        public double apply(double x, double y) {
            return x - y;
        }
    };

    private final String symbol;

    Operation(String symbol) {
        this.symbol = symbol;
    }

    @Override
    public String toString() {
        return symbol;
    }

    public abstract double apply(double x, double y);

    private static final Map<String, Operation> stringToEnum = Stream.of(values()).collect(toMap(Object::toString, e -> e));

    public static Optional<Operation> fromString(String symbol) {
        return Optional.ofNullable(stringToEnum.get(symbol));
    }

}

예시2. 각 키와 해당 키의 특정 원소를 연관짓는 맵을 생섯하는 수집기

Map<Artist, Album> topHits = albums.collect(toMap(Album::article, a->a, maxBy(comparing(Album::sales))));

groupingBy

String result= list.stream().collect(Collectors.joining()); System.out.println(result); // output : ABCD

result= list.stream().collect(Collectors.joining(",")); System.out.println(result); // output: A,B,C,D

result= list.stream().collect(Collectors.joining("-","[","]")); System.out.println(result); // output: [A-B-C-D]


## 결론
- 스트림 파이프라인 프로그래밍의 핵심은 부작용 없는 함수 객체에 있다. 
- 스트림뿐 아리나 스트림 관련 객체에 건네지는 모든 함수 객체가 부작용이 없어야 한다. 
- forEach는 수행한 계산 결과를 보고할 때만 이용해야 한다.
- 스트림을 올바로 사용하려면 collector를 잘 알아둬야 한다. 
- 수집기 팩터리 : toList, toSet, toMap, groupingBy, joining