자바 8과 9에서는 스트림 API가 지원하는 필터링, 슬라이싱, 매핑, 검색, 매칭, 리듀싱과 같은 다양한 연산이 있다.
그중 나는 매핑과 리듀싱을 함께 사용하는 맵리듀스에 대해 자세히 알아보고자 정리하였다.
1. 매핑
- 특정 데이터를 선택하는 기능으로 데이터 처리 과정에서 자주 수행된다.
- map과 flatMap 메서드 지원
1) map 메서드
인수로 제공된 함수는 각 요소에 적용되며 적용된 결과가 새로운 요소로 매핑된다.
List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName)
.map(String::length)
.collect(toList());
map 메서드를 통해 요리명을 추출하고 추출된 요리명의 길이를 반환하는 코드이다.
2) map 메서드의 문제점?
리스트 내부 문자열의 각 단어를 구하기 위해 split()을 사용하면
String -> String[] 즉 배열로 반환값이 변경되면서 원하는 값을 얻지 못할 수도 있다.
ArrayList<String> words = new ArrayList<>();
words.add("Hello");
words.add("World");
// words = ["Hello", "World"]
words.stream()
.map(word -> word.split("")) // [["H","e","l","l","o"], ["W","o","r","l","d"]]
.distinct() // distinct() -> 중복제거 불가
.collect(toList()); // 오류 발생
3) 해결방법
flatMap 메서드 사용 - 평면화 작업을 통해 각 배열 안의 요소를 가져올 수 있다.
ArrayList<String> words = new ArrayList<>();
words.add("Hello");
words.add("World");
// words = ["Hello", "World"]
words.stream()
.map(word -> word.split("")) // [["H","e","l","l","o"], ["W","o","r","l","d"]]
.flatMap(Array::stream) // 생성된 스트림을 하나의 스트림으로 평면화
.distinct() // ["H","e","l","l","o","W","o","r","l","d"]
.collect(toList()); // distinct() -> 중복제거 가능
// ["H","e","l","o","W","r","d"] 반환
2. 리듀싱
- 스트림이 하나의 값으로 줄어들때까지 요소를 반복해서 조합한다.
(ex. 리스트 내 각 요소의 덧셈이나 곱셈 또는 최솟값, 최댓값 반환)
- 스트림 요소를 조합해서 스트림의 모든 요소를 반복적으로 처리해야 한다.
- 위와 같은 과정(모든 스트림 요소를 처리해서 값으로 도출하는)을 리듀싱이라고 한다
- 맵과 리듀스를 연결하는 기법을 맵 리듀스 패턴이라고 한다.
3. 자바 - 맵리듀싱 구현 예시
import java.util.ArrayList;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MapReduce {
public static void main(String[] args) {
ArrayList<String> strs = new ArrayList<>();
strs.add("The die is cast");
strs.add("When they go low, we go high");
strs.add("I was never less alone than When by myself");
// wordCount 1
Map<String, Long> map =
strs.stream()
.flatMap(x -> Stream.of(x.split(" ")))
.collect(Collectors.groupingBy(x -> x, Collectors.counting()));
map.forEach((x, y) -> System.out.printf("key: %s, value: %d\n", x, y));
// wordCount 2
Map<Object, Long> map2 =
strs.stream()
.flatMap(x -> Stream.of(x.split(" ")))
.map(x -> new WordCount(x, 1))
.collect(Collectors.groupingBy(WordCount::getWord, Collectors.counting()));
System.out.println();
map2.forEach((x, y) -> System.out.printf("key: %s, value: %d\n", x, y));
}
}
class WordCount {
public String word;
public int count;
public WordCount(String word, int count) {
this.word = word;
this.count = count;
}
public String getWord() {
return word;
}
public int getCount() {
return count;
}
}
출력 결과
key: die, value: 1
key: myself, value: 1
key: was, value: 1
key: go, value: 2
key: I, value: 1
key: low,, value: 1
key: is, value: 1
key: less, value: 1
key: we, value: 1
key: The, value: 1
key: never, value: 1
key: cast, value: 1
key: alone, value: 1
key: high, value: 1
key: When, value: 2
key: by, value: 1
key: than, value: 1
key: they, value: 1
key: die, value: 1
key: myself, value: 1
key: was, value: 1
key: go, value: 2
key: I, value: 1
key: low,, value: 1
key: is, value: 1
key: less, value: 1
key: we, value: 1
key: The, value: 1
key: never, value: 1
key: cast, value: 1
key: alone, value: 1
key: high, value: 1
key: When, value: 2
key: by, value: 1
key: than, value: 1
key: they, value: 1
Reference
참고 도서명: 모던 자바 인 액션
https://cornswrold.tistory.com/546
https://gist.github.com/developer-sdk/46dc46e04b0f1d0defc1ca90cea3833c
'book > modern java in action' 카테고리의 다른 글
Chapter 8 컬렉션 API 개선 (0) | 2023.01.23 |
---|---|
Chapter 6 스트림으로 데이터 수집(Collect, Collector, Collectors) (0) | 2023.01.07 |
Chapter 4 스트림 소개 (0) | 2022.12.24 |
Chapter 3 람다 표현식 (0) | 2022.12.23 |
Chapter 2 동작 파라미터화 코드 전달하기 (0) | 2022.12.03 |
댓글