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

스트림 패러다임

  • 함수형 프로그래밍에 기초한 패러다임

  • 스트림이 제공하는 표현력, 속도, 병렬성을 얻기 위해서 스트림 API와 스트림 패러다임을 받아들여야 한다.

  • 계산을 일련의 변환으로 재구성하는 것이 핵심

  • 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리 하는 순수 함수여야 한다.

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

forEach의 용도

  • forEach 연산은 스트림 계산 결과를 보여줄 때만 사용한다.

  • forEach 내부에서 연산 작업을 수행할 경우 스트림 코드를 가장한 반복적 코드가 되버릴 수 있다. 스트림 API의 이점을 살리지 못하여 같은 기능의 반복 코드 보다 길고, 읽기 어렵고 유지보수에도 안좋다.

Collectors

  • 축소 전략을 캡슐화한 블랙박스 객체: 스트림의 원소들을 하나의 객체(보통은 컬렉션 객체)로 취합한다.

  • toList, toSet, toCollection 메서드를 사용해 컬렉션 타입을 반환할 수 있으며, 정적 임포트하여 사용 시 가독성이 좋아진다.

List<String> topTen = freq.keySet().stream()
    .sorted(comparing(freq::get).reversed())
    .limit(10)
    .collect(toList());
  • comparing 메서드를 사용해 키 추출 함수 get을 입력받아 value를 비교해 역순으로 저장한다.

toMap 메서드

  • (스트림 원소를 키에 매핑하는 함수,스트림 원소를 값에 매핑하는 함수, 병합 함수) 세가지를 인수로 받아 맵 형식으로 반환한다.

병합 함수

  • 어떤 키와 그 키에 연관된 원소 중 하나를 골라 연관 짓는 맵을 만들 때 유용하다.

  • 아래는 다양한 음악가의 앨범들을 담은 스트림에서 (음악가, 음악가의 베스트 앨범) 맵을 생성하는 예제이다.

Map<Artist, Album> topHits = albums.collect(
	toMap(Album::artist, a->a, maxBy(comparing(Album::sales))));
  • 아래는 마지막에 들어온 값을 키에 저장하도록 병합 함수를 지정한 예제이다.

toMap(KeyMapper, valueMapper, (oldVal, newVal) -> newVal)

groupingBy 메서드

  • gropingBy(분류함수, 맵 팩토리, 다운스트림 수집기) 형태이다.

  • 분류 함수를 입력받아, (카테고리, 카테고리에 속하는 원소들의 리스트) 형태의 맵을 담은 수집기를 반환한다.

분류 함수: 입력받은 원소가 속하는 카테고리 반환

  • 키에 대해 리스트 외의 값을 갖도록 하려면, 다운스트림 수집기를 명시해야 한다.

다운스트림 수집기

  • 맵 팩토리를 지정할 수 있다.

minBy, maxBy 메서드

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

  • 수집과는 관련이 없지만 Collections에 정의되어 있다.

joining 메서드

  • joining(구분문자, 접두문자, 접미문자)

  • 원소들을 concat하는 수집기를 반환

  • CharSequence 인스턴스의 스트림에만 적용 가능

Last updated