스트림 패러다임의 핵심은 계산을 일련의 변환으로 재구성하는 부분이다. 이때 각 변환 단계는 가능한 한 이전 단계의 결과를 받아 처리하는 순수 함수여야 한다. 순수 함수란 오직 입력만이 결과에 영향을 주는 함수를 말한다.
public static void main(String[] args) {
String str = "hello this is study of Effective java and i am newbie in java";
Stream<String> words = Arrays.stream(str.split(" "));
Map<String, Long> badFreq = new HashMap<>();
words.forEach(word -> {
badFreq.merge(word.toLowerCase(), 1L, Long::sum);
});
}
위 코드는 모은 작업이 종단 연산인 forEach() 에서 이루어진다. 이때 외부 상태를 수정하는 람다를 실행하면서 문제가 생긴다. 종단 연산이 연산 결과를 보여주는 일 이상의 행위를 수행하고 있는 것이다. forEach 가 그저 스트림이 수행한 연산 결과를 보여주는 일 이상을 하는 것이다.
public static void main(String[] args) {
String str = "hello this is study of Effective java and i am newbie in java";
Stream<String> words = Arrays.stream(str.split(" "));
Map<String, Long> freq;
freq = words.collect(groupingBy(String::toLowerCase, counting()));
}
수집기는 스트림을 사용하기 위해서 반드시 알아야하는 개념이다. 수집기를 사용하면 스트림의 원소를 손쉽게 컬렉션으로 모을 수 있다. 수집기는 총 세 가지로, toList(), toSet(), toCollection(collectionFactory) 가 있다. 이들은 차례로 리스트, 집합, 프로그래머가 지정한 컬렉션 타입을 반환한다.
List<String> topTen = freq.keySet().stream().sorted(comparing(freq::get).reversed())
.limit(10)
.collect(Collectors.toList());
간단한 맵 수집기는 toMap(keyMapper, valueMapper)
의 인수 두개를 받는 형태이다. 다음 코드를 보자.
private static final Map<String, Operation> stringToEnum =
Stream.of(values()).collect(
toMap(Object::toString, e -> e));
다음 코드는 인수 3개를 받는 toMap(keyMapper, valueMapper, BinaryOperator<U>)
형태의 맵 수집기이다. 여기서 U는 해당 맵의 값 타입이다. 같은 키를 공유하는 값들은 이 병합 함수를 사용해 기존 값에 합쳐진다.
Map<Artist, Album> topHits = albums.collect(
toMap(Album::aritst, a->a, maxBy(comparing(Album::sales))));
Album::artist
를 사용하고, key로 추출된 음악가와 그 음악가의 베스트 앨범을 짝지었다.toMap(keyMapper, valueMapper, (oldVal, newVal) -> newVal)