brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Feb 25. 2019

Effective Java3/E - 람다와 스트림

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

Effective Java 3/E - 람다와 스트림


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


스트림에서는 순수 함수(Pure function)를 이용해야 합니다.

오직 입력만이 결과에 영향을 주는 함수

다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않음


이를 위해서는 부작용(side effect)이 없는 함수를 이용해야 합니다.




#01 Side effect 없는 함수


#01 잘못된 코드


스트림, 람다, 메서드 참조 모두 이용했고, 원하는 텍스트 파일에서 단어별 수를 Map안에 저장하는 결과도 얻을 수 있습니다.


하지만 종단 연산인 forEach 문에서 외부 상태(Map<String, Long> freq)를 수정하고 있습니다.

종단 연산 forEach가 스트림이 수행한 연산 결과를 보여주는 일 이상을 하기 때문에 side effect가 발생할 수 있습니다.


#02 반복문


따라서 이 코드는 스트림 코드를 가장한 반복적 코드로 볼 수 있는데, 일반 반복적 코드보다 가독성도 안 좋고 유지보수도 힘듭니다.


#03 올바른 코드


스트림을 제대로 이용했고, 이전 코드보다 더 짧고 명확해졌습니다.




#02 java.util.stream.Collectors


#02-01 toList(), toSet(), toCollection(collectionFactory)


Collectors를 이용하면 스트림의 원소들을 손쉽게 Collection으로 모을 수 있습니다.

부작용이 없는 함수를 이용하기 위해 Collectors 클래스의 메서드들을 소개하네요.


toList()toSet()toCollection(collectionFactory)를 이용할 수 있습니다.


#04 toList, toCollection




#02-02 toMap()


Collectors의 대부분 메서드들은 Map을 취합하는 메서드입니다.

그중 toMap 메서드를 보면, 아래와 같이 스트림 원소를 Key에 mapping 하는 함수(keyMapper)와 Value에 mapping 하는 함수(valueMapper)를 인자로 받습니다.

#05 toMap


toMap 메서드를 이용해 아래와 같이 각 원소(Person)의 name을 Key, address를 Value로 이용하는 Map 객체를 얻을 수 있습니다.

만약 중복되는 Key가 있다면, IllegalStateException 예외를 발생시킵니다.

#06 toMap example


이런 경우엔 아래와 같이 3개의 인자를 받는 toMap 메서드를 이용할 수 있습니다.

Key mapper와 value mapper 외에, BinaryOperator<U> 병합 메서드를 인자로 받고 있습니다.

만약 같은 Key를 공유하는 Value들은 이 병합 메서드를 이용해 기존 Value에 합쳐집니다.

#07 toMap


이제 중복되는 Key가 있더라도, IllegalStateException이 아닌, 병합된(s + ", " + a) Value 값이 mapping 됩니다.

#08 toMap example




#02-03 groupingBy()


이름 그대로 스트림의 요소들을 그룹화하는 메서드입니다.


분류 함수(classifier)를 인자로 받아, 스트림 원소들을 카테고리별로 모아 놓은 Map을 담은 List를 반납합니다.

#09 groupingBy

아래 처럼 스트림 원소(Person)들을 나이(age)에 따라 모아 놓은 Map<Integer age, List<Person by age>> 객체를 얻을 수 있습니다.

#10 groupingBy example


만약 List가 아닌 다른 형태의 Value를 갖는 Map을 생성하기 위해서는 downStream Collector도 명시해야 합니다.

#11 groupingBy


이렇게 하면, 스트림 원소(Person)의 나이(age) 별 합(counting())을 갖는 Map<Integer age, Long count> 객체를 얻을 수 있습니다.

#12 groupingBy example 




글의 제목은 부작용(side effect) 없는 함수를 사용하라 인데, 대부분의 내용은 Collectors 클래스의 메서드에 대한 설명이었습니다.


부작용이 없는 함수를 이용하기 위해서는 Collectors 클래스의 메서드들을 잘 알아야 한다는 내용인 거 같습니다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari