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

신명철·2022년 3월 12일
0

Effective Java

목록 보기
44/80

스트림의 핵심

스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다. 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다.

스트림 패러다임을 이해하지 못한 사용 예

public static void main(String[] args) {
    String str = "hello this is study of Effective java and i am newbie in java";
    Stream<String> words = Arrays.stream(str.split(" "));
    Map<String, Long> badFreq = new HashMap<>();
    words.forEach(word -> {
        badFreq.merge(word.toLowerCase(), 1L, Long::sum);
    });
}

위 코드는 모은 작업이 종단 연산인 forEach() 에서 이루어진다. 이때 외부 상태를 수정하는 람다를 실행하면서 문제가 생긴다. 종단 연산이 연산 결과를 보여주는 일 이상의 행위를 수행하고 있는 것이다. forEach 가 그저 스트림이 수행한 연산 결과를 보여주는 일 이상을 하는 것이다.

스트림을 활용해 올바르게 작성한 사용 예

public static void main(String[] args) {
    String str = "hello this is study of Effective java and i am newbie in java";
    Stream<String> words = Arrays.stream(str.split(" "));
    Map<String, Long> freq;
    freq = words.collect(groupingBy(String::toLowerCase, counting()));
}
  • forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고 계산하는 데는 쓰지 말자.

Collector

수집기는 스트림을 사용하기 위해서 반드시 알아야하는 개념이다. 수집기를 사용하면 스트림의 원소를 손쉽게 컬렉션으로 모을 수 있다. 수집기는 총 세 가지로, toList(), toSet(), toCollection(collectionFactory) 가 있다. 이들은 차례로 리스트, 집합, 프로그래머가 지정한 컬렉션 타입을 반환한다.

수집기를 사용한 예

List<String> topTen = freq.keySet().stream().sorted(comparing(freq::get).reversed())
    .limit(10)
    .collect(Collectors.toList());
  • 위 코드는 freq 에서 빈도 수 상위 10개를 추출하는 스트림 파이프라인이다.

간단한 맵 수집기는 toMap(keyMapper, valueMapper)의 인수 두개를 받는 형태이다. 다음 코드를 보자.

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

다음 코드는 인수 3개를 받는 toMap(keyMapper, valueMapper, BinaryOperator<U>) 형태의 맵 수집기이다. 여기서 U는 해당 맵의 값 타입이다. 같은 키를 공유하는 값들은 이 병합 함수를 사용해 기존 값에 합쳐진다.

Map<Artist, Album> topHits = albums.collect(
	toMap(Album::aritst, a->a, maxBy(comparing(Album::sales))));
  • 위 코드는 앨범 스트림을 맵으로 반환하는데, 자신의 키 추출 함수로는 Album::artist를 사용하고, key로 추출된 음악가와 그 음악가의 베스트 앨범을 짝지었다.
toMap(keyMapper, valueMapper, (oldVal, newVal) -> newVal)
  • 인수가 3개인 toMap은 충돌이 나면 마지막 값을 취하는 수집기를 만들 때도 유용하다.
profile
내 머릿속 지우개

0개의 댓글