-
item 46 스트림에서는 부작용 없는 함수를 사용하라책/이펙티브 자바 2022. 4. 12. 16:56
ITEM 46 스트림에서는 부작용 없는 함수를 사용하라
스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성 하는것
- 각 변환 단계는 가능한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다
- 순수 함수 : 오직 입력만이 결과에 영향을 주는 함수
- 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않고 함수 스스로도 다른 상태를 변경하지 않음
- 따라서 중간 단계든 종단 단계든 스트림 연산에 건네는 함수 객체는 모두 부작용이 없어야 한다
스트림 패러다임을 이해하지 못한 api 사용법 텍스트 파일에서 단어별 수를 세어 빈도표로 만든다 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에서 모든 작업을 수행하며 외부 상태(빈도표)를 수정하는 람다를 실행하면서 문제가 생긴다
- forEach가 스트림이 수행한 연산 결과를 보여주는 일만 하는것이 아니라 람다가 상태를 수정하고 있다
같은 동작을 하는 좋은 예시를 보자
Map<String, Long> freq; try (Stream<String> words = new Scanner(file).tokens()) { freq = words.collect(groupingBy(String::toLowerCase, counting())); }
위 코드는 스트림 api를 잘 사용했으며 짧고 명확하다
첫번째 코드처럼 짜는 사람은 대부분 익숙하기 때문에 그럴것이다
- 평소에 자주 사용하던 for-each 반복문과 forEach 종단이 비슷하게 생겼다
- forEach 연산은 종단 연산 중 기능이 가장 적으며 가장 스트림 답지 않다
- 대놓고 반복적이라 병렬화도 어렵다
- forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고 계산할때는 쓰지말자
또한 위 코드는 collect(수집기)를 사용하는데 수집기는 스트림을 사용하려면 꼭 배워야 하는 개념이다
- java.util.stream.Collectors 클래스는 메서드를 39개나 가지고 있고 타입 매개변수가 5개나 되는것도 있다
- 하지만 복잡한 세부 내용을 잘 몰라도 이 api의 장점을 대부분 활용 가능하다
- 익숙해지기 전까지는 Collector 인터페이스를 잠시 잊고 축소(reduction) 전략을 캡슐화한 블랙박스 객체라고 생각하자
- 축소는 스트림의 원소들을 객체 하나에 취합한다는 뜻
- 수집기가 생성하는 객체는 일반적으로 컬렉션이며 collector라는 이름을 쓴다
- 수집기를 사용하면 스트림의 원소를 손쉽게 컬렉션으로 모을수 있으며 수집기는 toList(), toSet(), toCollection(collectionFactory) 세가지가 있다
- 차례대로 리스트, 집합, 프로그래머가 지정한 컬렉션 타입을 반환한다
스트림 파이프라인 프로그래밍의 핵심은 부작용 없는 함수 객체에 있다
스트림뿐 아니라 스트림 관련 객체에 건네지는 모든 함수 객체가 부작용이 없어야 한다
종단 연산 중 forEach는 스트림이 수행한 계산 결과를 보고할 때만 이용하고 계산 자체에는 이용하지 말자
스트림을 올바로 사용하려면 수집기를 잘 알아둬야 한다
가장 중요한 수집기는 toList, toSet, toMap, groupingBy, joining 이다
'책 > 이펙티브 자바' 카테고리의 다른 글
item 48 스트림 병렬화는 주의해서 적용하라 (0) 2022.04.13 item 47 반환 타입으로는 스트림보다 컬렉션이 낫다 (0) 2022.04.12 item 45 스트림은 주의해서 사용하라 (0) 2022.04.11 item 44 표준 함수형 인터페이스를 사용하라 (0) 2022.04.11 item 43 람다보다는 메서드 참조를 사용하라 (0) 2022.04.11 - 각 변환 단계는 가능한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다