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

2022. 11. 7. 22:43· BE/Java
목차
  1. 아이템 46. 스트림에서는 부작용 없는 함수를 사용하라
반응형

EFFECTIVE JAVA(이펙티브 자바)

 

이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.


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

 

스트림 패러다임

  • 스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성 하는 부분이다.
  • 이 때, 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다.
  • 순수 함수
    • 오직 입력만이 결과에 영향을 주는 함수
    • 다른 가변 상태를 참조하지 않고, 함수 스스로도 다른 상태를 변경하지 않는다.
    • 이렇게 하려면 중간 단계든 종단 단계든 스트림 연산에 건네는 함수 객체는 모두 부작용이 없어야 한다.

 

ex 1) 스트림 패러다임을 이해하지 못한 예

Map<String, Long> freq = new HashMap<>();
try (Stream<String> words = new Scanner(file).tokens()) {
	words.forEach(word -> {
		freq.merge(word.toLowerCase(), 1L, Long::sum;
	});
}
  • 스트림, 람다, 메서드 참조를 사용했고 올바른 결과가 출력되지만 스트림 코드라 할 수 없다.
  • 스트림 코드를 가장한 반복적 코드이다. (같은 기능의 반복적 코드보다 길고, 어렵고, 유지보수에 안좋다.)

 

ex 2) 위의 예에서 스트림을 제대로 활용한 경우

Map<String, Long> freq;
try (Stream<String> words = new Scanner(file).tokens()) {
	freq = words.
					collection(groupingBy(String::toLowerCase, counting()));
}
  • 스트림 API를 제대로 사용했고 짧고 명확하다.
  • forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고, 계산하는 데는 쓰지 말자. (물론 가끔은 스트림 계산 결과를 기존 컬렉션에 추가하는 등의 다른 용도로 사용 가능)
  • 이 코드는 collector를 사용하는데, 스트림을 사용하려면 잘 알아둬야 한다.
    • collector를 사용하면 스트림의 원소를 쉽게 컬렉션으로 모을 수 있다.
    • collector는 총 3가지로, 리스트(toList()), 집합(toSet()), 프로그래머가 지정한 컬렉션 타입(toCollection(collectionFactory)) 이다.

 

ex 3) 빈도표에서 가장 흔한 단어 10개를 뽑아내는 파이프라인

List<String> topTen = freq.keySet().stream()
	.sorted(**comparing(freq::get)).revered()**)
	.limit(10)
	.collect(toList());
  • comparing 메서드는 키 추출 함수를 받는 비교자 생성 메서드이다.
  • 한정적 메서드 참조이자, 여기서 키 추출 함수로 쓰인 freq::get은 입력받은 단어(키)를 빈도표에서 찾아 그 빈도를 반환한다.
  • 그런 다음 흔한 단어가 위로 오도록 비교자를 역순으로 정렬한 후에 스트림에서 단어를 10개 뽑아 리스트에 담는다.

 

 

Collectors의 다른 메서드들

java.util.stream.Collectors의 API 문서(http://bit.ly/2MvTOAR)을 참고하면 좋다.

  1. toMap(keyMapper, valueMapper)
private static final Map<String, Operation> stringToEnum =
        Stream.of(values()).collect(
						**toMap(Object::toString, e -> e));**
  • 스트림 원소를 키에 매핑하는 함수와 값에 매핑하는 함수를 인수로 받는다.
  • 이 간단한 toMap 형태는 스트림의 각 원소가 고유한키에 매핑되어 있을 때 적합하다.
  • 스트림 원소 다수가 같은 키를 사용한다면 파이프라인이 IllegalStateException을 던질 것이다.

 

Map<Artist, Album> topHits = albums.collect(
	toMap(Album::artist, a->a, maxBy(comparing(Album::sales))));
  • 인수 3개를 받는 toMap은 어떤 키와 그 키에 연관된 원소들 중 하나를 골라 연관 짓는 맵을 만들 때 유용하다.
  • 위의 코드는 앨범 스트림을 맵으로 바꾸는데, 이 맵은 각 음악가와 그 음악가의 베스트 앨범을 짝지은 것

 

toMap(keyMapper, valueMapper, (oldVal, newVal) -> newVal)
  • 인수가 3개인 toMap은 충돌이 나면 마지막 값을 취하는 수집기도 만들 때 편하다.

 

 

2. groupingBy

words.collect(groupingBy(word -> alphabetize(word))
  • 입력으로 분류 함수를 받고 출력으로는 원소들을 카테고리별로 모아 놓은 Map을 담은 Colelctor를 반환한다.

 

3. partitionBy

  • 분류 함수 자리에 프레디키트를 받고 키가 Boolean인 Map을 반환한다.

 

4. counting

  • counting 메서드가 반환하는 수집기는 다운스트림 수집기 전용이다.
  • Stream의 count 메서드를 직접 사용하여 같은 기능을 수행할 수 있으니 collect(counting()) 형태로 사용할 일은 없다.
  • Collections에는 이런 속성의 메서드가 16개나 더 있으며, 그 중 9개는 이름이 summing, averaging, summarizing 으로 시작하며, 각각 int, long, double 스트림용으로 하나씩 존재한다.

 

5. minBy / maxBy

  • 인수로 받은 비교자를 이용해 스트림에서 값이 가장 작은 / 큰 원소를 반환한다.

 

6. joining

  • CharSequence 인스턴스의 스트림에만 적용할 수 있다.
  • 이 중 매개변수가 없는 joining은 단순히 원소들을 연결하는 수집기를 반환한다.
  • 이 메소드는 연결 부위에 구분문자를 삽입하는데, 예컨대 구분문자로 쉼표(,)를 입력하면 CSV 형태의 문자열을 만들어준다.

 

정리

  • 스트림 파이프라인 프로그래밍의 핵심은 부작용 없는 함수 객체에 있다.
  • 스트림 뿐 아니라 스트림 관련 객체에 건네어지는 모든 함수 객체는 부작용이 없어야 한다.
  • 종단 연산 중 forEach는 스트림이 수행한 계산결과를 보고할 때만 이용해야한다.
  • 스트림을 올바르게 사용하려면 collector를 잘 알아둬야한다.
반응형

'BE > Java' 카테고리의 다른 글

[Effective Java] 아이템 48. 스트림 병렬화는 주의해서 적용하라  (0) 2022.11.09
[Effective Java] 아이템 47. 반환 타입으로는 스트림보다 컬렉션이 낫다.  (0) 2022.11.08
[Effective Java] 아이템 45. 스트림은 주의해서 사용하라  (1) 2022.11.04
[Effective Java] 아이템 44. 표준 함수형 인터페이스를 사용하라  (0) 2022.11.03
[Effective Java] 아이템 43. 람다보다는 메서드 참조를 사용하라  (0) 2022.11.02
  1. 아이템 46. 스트림에서는 부작용 없는 함수를 사용하라
'BE/Java' 카테고리의 다른 글
  • [Effective Java] 아이템 48. 스트림 병렬화는 주의해서 적용하라
  • [Effective Java] 아이템 47. 반환 타입으로는 스트림보다 컬렉션이 낫다.
  • [Effective Java] 아이템 45. 스트림은 주의해서 사용하라
  • [Effective Java] 아이템 44. 표준 함수형 인터페이스를 사용하라
멍목
멍목
개발 관련 새롭게 알게 된 지식이나 좋은 정보들을 메모하는 공간입니다.
반응형
멍목
김멍목의 개발블로그
멍목
전체
오늘
어제
  • 분류 전체보기 (514)
    • BE (190)
      • Spring (21)
      • Java (141)
      • Kotlin (6)
      • JPA (22)
    • FE (33)
      • Javascript (16)
      • Typescript (0)
      • React (5)
      • Vue.js (9)
      • JSP & JSTL (3)
    • DB (32)
      • Oracle (22)
      • MongoDB (10)
    • Algorithm (195)
    • Linux (8)
    • Git (6)
    • etc (42)
    • ---------------------------.. (0)
    • 회계 (4)
      • 전산회계 2급 (4)
    • 잡동사니 (2)

블로그 메뉴

  • 홈
  • 관리

공지사항

인기 글

태그

  • JPA
  • MongoDB 기초부터 실무까지
  • 이펙티브자바
  • 이펙티브 자바
  • 프로젝트로 배우는 Vue.js 3
  • 자기공부
  • 더 자바 애플리케이션을 테스트하는 다양한 방법
  • 알고리즘 공부
  • 자기 개발
  • MongoDB with Node.js
  • junit5
  • 자기 공부
  • 자바 테스팅 프레임워크
  • Oracle
  • 자기개발
  • java 8
  • 더 자바 Java 8
  • 코테 공부
  • 코테공부
  • 코틀린
  • Effective Java
  • JPA 공부
  • 자바 개발자를 위한 코틀린 입문
  • 자바 공부
  • MongoDB 공부
  • 알고리즘공부
  • vue3 공부
  • Java to Kotlin
  • 전산회계 2급 준비
  • 자바공부

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.0
멍목
[Effective Java] 아이템 46. 스트림에서는 부작용 없는 함수를 사용하라
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.