모던 자바 인 액션 08. 스트림 활용(2)
JAVA/모던 자바 인 액션

모던 자바 인 액션 08. 스트림 활용(2)

매핑

객체에서 특정 데이터를 선택하는 작업은 매우 흔한 일입니다. (SQL 특정 열 선택 등)

이번에는 이 특정 데이터를 선택하는 기능을 알아봅시다.

 

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

map( )

'map( )'은 함수를 인수로 받으며, 그 결과는 새로운 요소로 매핑이 됩니다. (새로운 스트림이 만들어진다 생각하시면 편합니다.)

  void mappingDishNameLength() {
      List<Integer> dishNameLength = menu.stream()    // Type : Stream<Dish>
              .map(Dish::getName)                     // Type : Stream<String>
              .map(String::length)                    // Type : Stream<Integer>
              .collect(toList());			// Type : List<Integer>
      System.out.println("메뉴명 길이 = " + dishNameLength);
  }

스트림 평면화

이전에 배운 'distinct( )'를 이용해 고유 문자로 이루어진 리스트를 반환해 봅시다

문제가 발생했습니다. 'map( )'에서 사용한 'split( )'이 'String[ ]' 타입으로 반환하는 메서드입니다!

// Q: ["Hello", "World"]    Ans: ["H", "e", "l", "o", "W", "r", "d"] 
static void badDistinctWord() {
	List<String[]> distinctList = StreamMapping
    	.distinctList.stream() 
    	.map(word -> word.split("")) // Stream<String[]> 
    	.distinct() 
    	.collect(toList()); 
    System.out.println("중복되지 않는 문자 = " + distinctList); 	// 결과 : "Hello", "World" 
    
}

그래서 타입을 맞춘다 하더라도 'distinct( )'의 중복 검사는 String[ ] 배열 단위로 이루어지고, 결국 우리가 뜻하는대로 작동하지 않게 됩니다.

그림 2 타입의 오류

그래서 우리가 해야하는 것은 1차적으로 'String[ ]' 배열이 아닌 String 타입으로 분해를 해야 합니다.

그러기 위해서는 먼저 각 단어를 개별 문자열로 이루어진 배열로 만든 후 각 배열을 별도의 스트림으로 다시한번 만들어야 합니다!

 

 

2차시도

배열을 문자열로 한번 더 쪼개기 위해서는 stream을 두번 사용하면됩니다! 

private static List<Stream<String>> flatMappingExampleV2(List<String> list) {
    return list.stream()
        .map(s -> s.split(""))		// Stream<String[]>
        .map(Arrays::stream)		// Stream<Stream<String>>
        .distinct()
        .collect(toList());
}

여전히 문제가 있습니다.

'Arrays::stream'은 최종연산을 수행하지 않았기 때문에, 아직 Stream의 형태로 남아있습니다.

그래서 Stream<Stream<String>> 타입이 됩니다.

그래서 계층화된 Stream을 평면화할 필요가 있습니다. 이를 위해 'flatMap( )'이 필요합니다!

 

flatMap( )

flatMap( )은 말 그대로 Map을 Flat하게 계층화된 Stream<Stream<String>>을 Stream<String>으로 평면화시킬 수 있습니다.

private static List<Stream<String>> flatMappingExampleV2(List<String> list) {
      return list.stream()    // Stream<String>
              .map(word -> word.split(""))           // Stream<String[]>
              .flatMap(Arrays::stream)               // Stream<Stream<String>> -> Stream<String>
              .distinct()
              .collect(toList());
}

그림 3 FlatMap

 

요약

map( )

  • map()은 함수를 인수로 받으며, 그 결과들로 새로운 스트림을 만들어서 매핑.

flatMap( )

  • 복합 스트림을 하나의 스트림으로 평면화 시켜서 새로운 스트림에 매핑.
    • ex: Stream<Stream<String>> -> 새로운 Stream<String>

참고 자료

  • 모던 자바 인 액션
  • JAVA Reference