들어가며
스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다. 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다. 다음은 스트림 패러다임을 이해하지 못한 채 API만 사용한 예이다.
1 | public static void main(String[] args) { |
위 코드는 모든 작업이 종단 연산인 forEach에서 이뤄진다. 이때 외부 상태를 수정하는 람다를 실행하면서 문제가 생긴다. 종단 연산이 그저 스트림의 연산 결과를 보여주는 일 이상의 행위를 하고있는 것이다. forEach 연산은 스트림 계산 결과를 보고할 때만 사용하고 계산하는데는 쓰지 말자. 다음 코드는 올바르게 작성된 코드이다.
1 | public static void main(String[] args) { |
collector
수집기는 스트림을 사용하기 위해서 반드시 알아야하는 개념이다. 다음 코드를 보자.
1 | List<String> topTen = freq.keySet().stream().sorted(comparing(freq::get).reversed()) |
위 코드는 freq 빈도표에서 빈도수 상위 단어 10개를 추출하는 스트림 파이프라인이다. comparing(freq::get)으로 value를 가져와 역순으로 정렬한 뒤 상위 10개를 추출한다.
간단한 맵 수집기는 toMap(keyMapper, valueMapper)
의 인수 두개를 받는 형태이다. 인수 3개를 받는 맵 수집기도 있다. toMap(keyMappter, valueMapper, userPrinciple)
의 형태이다. 3번째 인수에 삽입하고자 하는 결과값을 넣을 수 있다. 예를 들어 최신 데이터로 교체하고 싶다면 (oldVal, newVal) -> newVal
로, 최대값을 맵핑시키고 싶다면 maxBy(comparing(oldval))
의 형태로 작성하면 된다.
groupingBy는 입력으로 분류 함수를 받고 출력으로는 원소들을 카테고리별로 모은 Map을 담은 수집기를 반환한다. 분류 함수는 입력받은 원소가 속하는 카테고리를 반환한다.
1 | Map<String, Long> freq = words.collect(groupingBy(String::toLowerCase, counting())); |
minBy 와 maxBy는 수집과는 관련 없는 메소드이다. 이 두 메소드는 인수로 받은 비교자를 통해 스트림에서 값이 작은 혹은 큰 원소를 반환한다.