Learnitweb

map() vs flatMap() in Java

Overview

Both map() and flatMap() are intermediate operations in Java’s Stream API. They are used to transform elements of a stream, but they differ in what they return and how they handle nested structures.

1. map() – Transform Each Element One-to-One

Purpose:

  • Applies a function to each element of the stream.
  • The function transforms the element and returns a new value.
  • The number of output elements remains the same as the number of input elements.

Method Signature:

<R> Stream<R> map(Function<? super T, ? extends R> mapper)

Characteristics:

  • Input: Stream of type T
  • Output: Stream of type R (same number of elements)
  • Common use: Convert String to Integer, extract fields from objects, etc.

Example 1: Convert List of Strings to Uppercase

List<String> names = Arrays.asList("alice", "bob", "charlie");

List<String> upperNames = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println(upperNames);  // [ALICE, BOB, CHARLIE]

Example 2: Extract field from objects

List<Person> people = List.of(
    new Person("Alice", 25),
    new Person("Bob", 30)
);

List<String> names = people.stream()
    .map(Person::getName)
    .collect(Collectors.toList());

System.out.println(names);  // [Alice, Bob]

2. flatMap() – Transform and Flatten One-to-Many

Purpose:

  • Applies a function to each element that returns a stream (or collection).
  • Then flattens the results into a single stream.
  • It’s used to flatten nested structures.

Method Signature:

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

Characteristics:

  • Input: Stream of type T
  • Function returns: Stream of R
  • Output: Flattened stream of all elements from all the returned streams
  • Useful when you need to transform one-to-many (e.g., splitting strings into words)

Example 1: Splitting Strings into Words

List<String> lines = Arrays.asList("Java is fun", "Streams are powerful");

List<String> words = lines.stream()
    .flatMap(line -> Arrays.stream(line.split(" ")))
    .collect(Collectors.toList());

System.out.println(words);
// [Java, is, fun, Streams, are, powerful]

If you used map() instead:

List<Stream<String>> mapped = lines.stream()
    .map(line -> Arrays.stream(line.split(" ")))
    .collect(Collectors.toList());

Now you have a List<Stream<String>>, not a List<String>. You’d need to flatten it manually if you want to work with the individual words.

Example 2: Flatten List of Lists

List<List<String>> listOfLists = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d"),
    Arrays.asList("e", "f")
);

List<String> flatList = listOfLists.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());

System.out.println(flatList);  // [a, b, c, d, e, f]

With map(), you’d get a Stream<List<String>>, which isn’t what you want if you’re trying to flatten it.

3. Summary Table

Featuremap()flatMap()
Return TypeStream<R>Stream<R>
Function ReturnsOne object per input (R)A Stream<R> (or collection) per input
Output Structure1-to-1 mapping1-to-many mapping + flattening
Use CaseTransforming elementsFlattening nested streams or collections
Keeps Nesting?YesNo (flattens the structure)

4. When to Use

  • Use map() when:
    • You want to transform data
    • Each element maps to one result
  • Use flatMap() when:
    • You want to flatten nested structures (lists of lists, streams of streams, etc.)
    • Each element maps to multiple results

5. Visual Illustration

Consider this input:

List<String> list = Arrays.asList("one,two", "three,four");

With map():

Stream<String[]> mapped = list.stream()
    .map(s -> s.split(","));

Result type: Stream<String[]> — A stream of arrays

With flatMap():

Stream<String> flatMapped = list.stream()
    .flatMap(s -> Arrays.stream(s.split(",")));

Result type: Stream<String> — Flattened stream of individual strings

6. Advanced Example: Nested Objects

class Department {
    String name;
    List<Employee> employees;

    public List<Employee> getEmployees() {
        return employees;
    }
}

Extract all employees across departments

List<Department> departments = // some list...

List<Employee> allEmployees = departments.stream()
    .flatMap(dept -> dept.getEmployees().stream())
    .collect(Collectors.toList());

If you used map() instead, you’d get List<Stream<Employee>>, not List<Employee>.

Conclusion

Understanding map() vs flatMap() is crucial for effective Java Stream API usage:

  • Use map() for simple transformation
  • Use flatMap() for flattening nested data structures
  • flatMap() is essentially map() + flatten