Contents
스트림 활용(매핑편)
   Sep 16, 2022     5 min read

Chapter5 (2)

스트림 활용 (매핑)

  • 특정 객체에서 특정 데이터를 선택하는 작업은 데이터 처리 과정에서 자주 수행되는 연산이다.
  • 스트림 API의 mapflatmap 메서드는 특정 데이터를 선택하는 기능을 제공한다.

스트림의 각 요소에 함수 적용하기

map() 메서드는 스트림에서 제공되며, 인수로 제공된 함수는 각 요소에 적용되며 함수를 적용한 결과가 새로운 요소로 매핑된다.

새로운 요소로 매핑된다는 것은 기존의 값을 ‘고친다(modify)라는 개념이 아니라 ‘새로운 버전을 만든다’라는 개념레 가까우므로 ‘변환(Transforming)에 가까운 ‘메핑이라는 단어를 사용한다.

➡️ 요리명 추출하기

List<String> dishNames = menu.stream()
  .map(Dish::getName)
  .collect(toList));

➡️ 결과

[pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon]

➡️ 각 단어가 포함하는 글자 수의 리스트를 반환

List<String> words = Arrays.asList("Modern", "Java", "In", "Action");
List<Integer> wordLengths = words.stream()
																 .map(String::length)
																 .collect(toList());

➡️ 결과

[6, 4, 2, 6]

그렇다면 각 요리명의 길이를 알아보자.

➡️ 요리명 길이 구하기

List<Integer> dishNameLengths = menu.stream()
																		.map(Dish::getName)
																	  .map(String::length)
																	  .collect(toList());

➡️ 결과

[4, 4, 7, 12, 4, 12, 5, 6, 6]

스트림 평면화

고유 문자로 이루어진 리스트를 반환해보자.

예를 들어 [”Hello”, “World”] 리스트가 있다면 결과로 [”H”, “e”, “l”, “o”, “W”, “r”, “d”]를 포함하는 리스트가 반환되어야 한다.

리스트에 있는 각 단어를 문자로 매핑한 다음에 distinct로 중복된 문자를 필터링해서 쉽게 문제를 해결수 있을 것같다.

➡️map을 통해서 고유 문자로 이루어진 리스트 만들기 (실패)

words.stream()
		 .map(word -> word.split(""))
		 .distinct()
		 .collect(toList());

하지만 … 결과는 다르게 나온다. 왜 그럴까 ?

map으로 전달한 람다는 각 단어의 String[ ] 을 반환한다는 점이 문제인거 같다.

그럼 map 메소드가 반환한 스트림의 형식은 Stream<String[]>이다.

내가 원하는 것은 문자열의 스트림을 표현할 Stream 이다.

➡️map을 이용해서 단어 리스트에서 고유 문자를 찾는 데 실패한 사례

Untitled

이 실패한 것을 어떻게 해결할 수 있을까? 먼저 말하자면 flatMap을 이용하면된다.

map과 Arrays.stream 활용

우선 배열 스트림 대신 문자열 스트림이 필요하다.

➡️문자열을 받아 스트릠을 만들어주는 메서드 → Arrays.stream()

String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);
words.stream()
		 .map(word -> word.split(""))  //<-- 각 단어를 개별 문자열 배열로 변환
		 .map(Arrays::stream) // <-- 각 배열을 별로의 스트림으로 생성
		 .distinct()
		 .collect(toList());

스트림 리스트(List<Stream>)가 만들어지면서 문제가 해결되지 않았다.

문제 해결

  • 각 단어를 개별 문자열로 이루어진 배열로 만든다.
  • 각 배열을 별도의 스트림으로 만들어야 한다.

flatMap 사용

List<String> uniqueCharacters = words.stream()
																		 .map(word -> word.split("") //<-- 각 단어를 개별 문자로 포함하는 배열로 변환
																		 .flatMap(Arrays::stream) //<-- 생성된 스트림을 하나의 스트림으롤 평면화
																		 .distinct()
																		 .collect(toList());

➡️flatMap은 각 배열을 스트림이 아니라 스트림의 콘텐츠로 매핑한다. 즉, map(Arrays::stream)과 달리 flatMap은 하나의 평면화된 스트림을 반환한다.

Untitled 1

  • 요약

flatMap 메서드는 스트림의 각 값을 다른 스트림으로 만든 다음에 모든 스트림을 하나의 스트림으로 연결하는 기능을 수행한다.

퀴즈 매핑

  1. 숫자 리스트가 주어졌을 때 각 숫자의 제곱근으로 이루어진 리스트를 반환하시오. 예를 들어 [1,2,3,4,5]가 주어지면 [1,4,9,16,25]를 반환해야 한다.

➡️정답

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> numbers = Arrays.stream()
														 .map(n -> n * n)
														 .collect(toList());
  1. 두 개의 숫자 리스트가 있을 때 모든 숫자 쌍의 리스트를 반환하시오. 예를 들어 두 개의 리스트[1, 2, 3]과 [3, 4]가 주어지면 [(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]를 반환해야 한다.

➡️정답

List<Integer> number1 = Arrays.asList(1, 2, 3);
List<Integer> number2 = Arrays.asList(3, 4);
List<int[]> pairs = number1.stream()
													 .flatMap(i -> number2.stream()
																						    .map(j -> new int[]{i, j})
													 )
													 .collect(toList());

  1. 이전 예제에서 합이 3으로 나누어 떨어지는 쌍만 반환하려면 어떻게 해야 할까 ? 예를 들어 (2, 4), (3, 3)을 반환해야 한다.

➡️정답

List<Integer> number1 = Arrays.asList(1, 2, 3);
  List<Integer> number2 = Arrays.asList(3, 4);
  List<int[]> pairs = number1.stream()
  .flatMap( i -> number2.stream()
  .filter(j -> (i + j) % 3 == 0)
  .map(j -> new int[]{i, j})
  )
  .collect(toList());

매핑을 회고하며..

진짜 SQL질의 같다는 생각이 너무 든다. 서브쿼리 짜는 거 같기도하고… 어렵다…학습하고 연습하여 익히자.. 신기하고 재밋네.. 알면 알수록.. 프로그래머스에서 문제들이 몇몇이 자바 8로 풀어진 문제들이 있는데 정말 간결하고 가독성이 좋다. 앞으로 나도 그렇게 풀 날만 기다리고 있다. 뭐 지금은 속도도 느리고 포기하지말고 끝까지하는 것이 오래가는것이다. 마지막 영화 짝패 그리고 함께자라기에서 인용한 글을 올리고 마치겠다.

강한놈이 오래가는게 아니라 오래 가는놈이 강한거더라. - 영화 짝패 대사