Post

Java map()과 flatMap()의 차이점

1. Optional의 Map과 Flatmap

map() 메소드는 Optional과 잘 작동한다. 함수가 필요한 정확한 유형을 반환하는 경우이다.

1
2
Optional<String> s = Optional.of("test");
assertEquals(Optional.of("TEST"), s.map(String::toUpperCase));

그러나 더 복잡한 경우에는 Optional을 반환하는 함수도 제공될 수 있다. 이러한 경우 map()을 사용하면 map() 구현이 내부적으로 추가 래핑을 수행하므로 중첩된 구조가 발생한다.

이 상황을 더 잘 이해하기 위한 또 다른 예이다.

1
2
3
4
assertEquals(Optional.of(Optional.of("STRING")), 
  Optional
  .of("string")
  .map(s -> Optional.of("STRING")));

보시다시피, 중첩 구조 Optional<Optional<String>>으로 끝난다. 작동하기는 하지만 사용하기가 꽤 번거롭고 추가적인 null 안전성을 제공하지 않으므로 플랫 구조를 유지하는 것이 좋다.

이것이 바로 flatMap()이 수행하는데 도움이 된다.

1
2
3
assertEquals(Optional.of("STRING"), Optional
  .of("string")
  .flatMap(s -> Optional.of("STRING")));

2. Streams의 Map과 Flatmap

두 방법 모두 Optional에 대해 유사하게 작동한다.

map() 메서드는 Stream 인스턴스의 기본 시퀀스를 래핑하는 반면 flatMap() 메서드를 사용하면 중첩된 Stream<Stream<R>> 구조를 피할 수 있다.

여기서 map()은 입력 Stream의 요소에 toUpperCase() 메서드를 적용한 결과로 구성된 Stream을 생성한다.

1
2
3
4
List<String> myList = Stream.of("a", "b")
  .map(String::toUpperCase)
  .collect(Collectors.toList());
assertEquals(asList("A", "B"), myList);

map()은 이러한 간단한 경우에 잘 작동한다. 하지만 입력으로 list의 lists 같이 더 복잡한 것이 있으면 어떻게 작동하는지 확인한다.

1
2
3
4
List<List<String>> list = Arrays.asList(
  Arrays.asList("a"),
  Arrays.asList("b"));
System.out.println(list);

목록 [[a], [b]] 목록을 인쇄한다.

이제 flatMap()을 사용한다.

1
2
3
4
System.out.println(list
  .stream()
  .flatMap(Collection::stream)
  .collect(Collectors.toList()));

이러한 조각의 결과는 [a, b]로 평면화된다.

flatMap() 메서드는 먼저 입력 Stream의 Streams을 문자열 스트림으로 평면화한다. 이후에는 map() 메서드와 유사하게 작동한다.

[출처 및 참고]

This post is licensed under CC BY 4.0 by the author.