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

Jimin Lim·2023년 7월 2일
0

Effective Java

목록 보기
30/38
post-thumbnail

아이템 46

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

스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다.

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

✅ 종단함수를 계산 연산으로 사용한 예시

Map<String, Long> freq = new HashMap<>();
try (Stream<String> words = new Scanner(file).tokens()) {
    words.forEach(word -> {
        freq.merge(word.toLowerCase(), 1L, Long::sum);
    });
}

위 코드는 텍스트 파일에서 단어별 수를 세어 빈도표를 만드는 일을 하는 것이다. 여기서 종단연산 forEach 부분은 freq(외부 상태)를 수정하고 있어 문제를 발생한다.

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

stream을 잘 사용하면 위와 같다. forEach 연산(종단연산)은 스트림 계산 결과를 보고할 때만 사용하고, 계산할 때는 쓰지말자. 가끔은 스트림 계산 결과를 기존 컬렉션에 추가하는 등 다른 용도로 사용할 수 있다.

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps 에 따르면,

forEach를 병렬로 실행하면 non-thread-safety 으로인해 잘못된 결과가 발생하고 이에 따라 필요한 동기화 로직을 추가하면 병렬 처리의 이점이 손상될 수 있다고 한다.

✅ Collector

  • toList(), toSet(), toCollection(collectionFactory) 로 스트림 원소를 컬렉션으로 모을 수 있다.
  • groupinyBy()로 입력으로 분류 함수를 받고 출력으로 원소들을 카테고리별로 모아 놓은 맵을 담은 수집기를 반환한다.
  • joining()은 원소들을 연결시키는 메서드다.

reference

https://github.com/woowacourse-study/2022-effective-java/tree/main/07%EC%9E%A5/%EC%95%84%EC%9D%B4%ED%85%9C_46

profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글