본문 바로가기
book/modern java in action

Chapter 5 스트림 활용

by eunoia_DB 2022. 12. 30.

자바 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

 

 

 

댓글