Java Stream map() Vs flatMap()

The map() and flatMap() are two important methods in the Stream interface. The map() method is mainly used for transformation whereas flatmap() method is used for transformation as well as flattening operations. In this post, we will explore Java Stream map() Vs flatMap().

We can think as –

map() – transformation/mapping(one to one)

flatMap() – transformation/mapping(one to many)+flattening

When to use map() and when to use flatMap()

We should go for map() if we have a list of Integer or List of String kind of data. For example for List<Integer> we will use map().

[2, 1, 6, 3]

We need to use flatMap() if we have a list of list of String kind of data. For example for List<List<String>> we will use flatMap().

[[jack@gmail.com, peter@gmail.com], [arya@gmail.com, alon@gmail.com], [ric@gmail.com, nick@gmail.com], [jon@gmail.com, rid@gmail.com]]

Let’s see an example that shows the difference between Java Stream map() Vs flatMap().

package com.javatute.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Employee {
    private Integer id;
    private List<String> emailIds;

    public Employee(Integer id, List<String> emailIds) {
        this.id = id;
        this.emailIds = emailIds;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public List<String> getEmailIds() {
        return emailIds;
    }

    public void setEmailIds(List<String> emailIds) {
        this.emailIds = emailIds;
    }
}

public class JavaStreamMapVsFlatMapExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee(2, Arrays.asList("jack@gmail.com", "peter@gmail.com")));
        employees.add(new Employee(1, Arrays.asList("arya@gmail.com", "alon@gmail.com")));
        employees.add(new Employee(6, Arrays.asList("ric@gmail.com", "nick@gmail.com")));
        employees.add(new Employee(3, Arrays.asList("jon@gmail.com", "rid@gmail.com")));

        //Using map() method - get id of each employee, since each employee has one id one to one mapping
        List<Integer> ids = employees.stream().map(e -> e.getId()).collect(Collectors.toList());
        System.out.println(ids);
        

        //Using flatmap() - get emailIds
        List<String> emailsIds = employees.stream().flatMap(e -> e.getEmailIds().stream()).collect(Collectors.toList());
        System.out.println(emailsIds);
    }
}

Output:-

[2, 1, 6, 3]
[jack@gmail.com, peter@gmail.com, arya@gmail.com, alon@gmail.com, ric@gmail.com, nick@gmail.com, jon@gmail.com, rid@gmail.com]

Note – If we use map() instead of flatMap() we will get the below output

 //problem Using map() 
 List<List<String>> emailsIdsList = employees.stream().map(e -> e.getEmailIds()).collect(Collectors.toList());
 System.out.println(emailsIdsList);

Output for the above code snippet:-

[[jack@gmail.com, peter@gmail.com], [arya@gmail.com, alon@gmail.com], [ric@gmail.com, nick@gmail.com], [jon@gmail.com, rid@gmail.com]]

Let’s see Java Stream map() Vs flatMap() in tabular format.

map()flatMap()
The map() method is used to process a list of Integer or List of String(stream of vaues) kind of data.The flatMap() is used to process list of list of Integer or list of list of String ( i.e stream of stream of values).
The map() method accepts Function interface of generic type
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
The flatMap() method accepts Function interface of generic type Stream.
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
The map() method is mainly used for mapping and transformation the stream.The flatMap() method is mainly used for mapping and transformation as well as flattening of the stream.
The map() is used for One-To-One mapping.The flatMap() method is used for one-to-many mapping
In the case of the map() method data gets transferred from Stream to Stream.In the case of the map() method data gets transferred from Stream<Stream> to Stream.

Let’s see Java stream map() vs flatMap() using different aspects.

1. Operation:

  • map(): Applies a function to each element in the stream and returns a new Stream containing the results.
  • flatMap(): Applies a function that produces a Stream for each element and flattens the resulting Streams into a single Stream.

2. Input and Output Types:

  • map(): Input and output types can be different. The function transforms each element into a new type.
  • flatMap(): Input and output types can be different. The function transforms each element into a Stream, which can have a different element type.

3. Stream Cardinality:

  • map(): Performs a one-to-one mapping. Each input element maps to exactly one output element.
  • flatMap(): Performs a one-to-many mapping. Each input element can map to zero or more output elements.

4. Flattening Effect:

  • map(): Does not flatten the Stream. The resulting Stream is a Stream of mapped values.
  • flatMap(): Flattens the Stream by combining the elements from each produced Stream into a single Stream.

5. Example:

map() Example:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredNumbers = numbers.stream()
                                       .map(x -> x * x)
                                       .collect(Collectors.toList());

In this example, the map() function squares each element in the numbers list, resulting in a new Stream of squared numbers [1, 4, 9, 16, 25].

flatMap() Example:

List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("apple", "banana"),
    Arrays.asList("cherry", "date"),
    Arrays.asList("grape", "kiwi")
);
List<String> flattenedList = nestedList.stream()
                                       .flatMap(innerList -> innerList.stream())
                                       .collect(Collectors.toList());

In this example, the flatMap() function takes a list of lists (nestedList) and flattens it into a single Stream of strings. The resulting flattenedList contains all the individual strings from the inner lists, resulting in [apple, banana, cherry, date, grape, kiwi].

6. Use Cases:

  • map() is useful for transforming elements in a one-to-one manner, such as changing data types or applying a simple operation to each element.
  • flatMap() is useful when you want to transform elements into multiple elements, such as when dealing with nested data structures or breaking down elements into sub-elements.

7. Common Operations:

  • map() is commonly used with functions like mapToInt(), mapToDouble(), or mapToObj() for type-specific transformations.
  • flatMap() is commonly used with operations that return Streams, such as flatMapToInt(), flatMapToDouble(), or flatMap() itself when dealing with nested structures.

8. Output Stream Type:

  • map(): Always returns a Stream of the same type as the input Stream.
  • flatMap(): Returns a Stream of elements of the type produced by the inner function.

9. Performance Consideration:

  • map(): Generally performs better than flatMap() for simple one-to-one transformations since it doesn’t involve flattening multiple Streams.
  • flatMap(): May involve more complex processing due to the potential flattening of multiple Streams, which can impact performance, especially when dealing with deeply nested data structures.

In summary, map() and flatMap() are powerful operations in Java Streams, and their choice depends on whether you want to perform one-to-one transformations or flatten nested structures and potentially create a one-to-many mapping.

This simplified tabular format highlights the key distinctions between map() and flatMap() in Java Streams.

That’s all about Java Stream map() Vs flatMap().

Other Java 8 examples