Java Streams Api - Difference Between Map and flatMap
map()
- Returns a stream consisting of the results of applying the given function to the elements of this stream.
- It wraps the underlying sequence in a Stream instance.
- Input is a stream and output is another (a new) stream.
flatMap()
- Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.).
- It allows avoiding nested Stream<Stream<R>> structure.
- Input is a stream of stream and output is another (a new) stream.
Generally speaking, a map operation wraps its return value inside its ordinal type, while flatMap does not. For example, in Optional
, a map operation would return Optional<String>
type, while flatMap would return String
type.
So, after using map()
, we need to unwrap (read “flatten”) the object to retrieve the value. Whereas after using flatMap()
, there is no such need as the object is already flattened.
Both map
and flatMap
are intermediate stream operations that receive a function and apply this function to all the elements of a stream.
The difference is that for map
, this function returns a value, but for flatMap
, this function returns a stream. The flatMap
operation “flattens” the streams into one.
Here’s an example where we take a map of users’ names and lists of phones and “flatten” it down to a list of phones of all the users using flatMap:
public class MapVsFlatmap {
public List<List<String>> getPhoneNumberLists(List<Person> people) {
return people.stream()
.filter(person -> CollectionUtils.isNotEmpty(person.getPhones()))
.map(p -> p.getPhones())
.collect(Collectors.toList());
}
public List<String> getAllDistinctPhoneNumbers(List<Person> people) {
return people.stream()
.filter(person -> CollectionUtils.isNotEmpty(person.getPhones()))
.map(p -> p.getPhones())
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}
public class MapVsFlatmapTests {
private MapVsFlatmap mapVsFlatmap = new MapVsFlatmap();
@Test
public void test_getAllPhoneNumbers() throws JsonProcessingException {
List<Person> people = TestsHelper.getPeople();
List<List<String>> phoneNumbers = mapVsFlatmap.getPhoneNumberLists(people);
assertEquals("[ [ \"555-1123\", \"555-3389\" ], [ \"555-2243\", \"555-5264\" ], [ \"555-6654\", \"555-3242\" ] ]",
(new ObjectMapper()).writerWithDefaultPrettyPrinter().writeValueAsString(phoneNumbers));
}
@Test
public void test_getAllDistinctPhoneNumbers() throws JsonProcessingException {
List<Person> people = TestsHelper.getPeople();
List<String> phoneNumbers = mapVsFlatmap.getAllDistinctPhoneNumbers(people);
assertEquals("[ \"555-1123\", \"555-3389\", \"555-2243\", \"555-5264\", \"555-6654\", \"555-3242\" ]",
(new ObjectMapper()).writerWithDefaultPrettyPrinter().writeValueAsString(phoneNumbers));
}
}