ZetCode

Java Stream flatMap

last modified May 24, 2025

This article demonstrates how to use the Java Stream flatMap method to transform and flatten nested data structures.

flatMap is an intermediate stream operation that maps each element to a stream and then flattens these streams into a single stream. It's particularly useful when working with nested collections or when each element can be transformed to multiple elements.

Basic flatMap syntax

The method signature for flatMap is:

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

The mapper function takes an element of type T and returns a Stream of R. The flatMap operation then concatenates all these streams into a single stream.

Flattening nested collections

The most common use case for flatMap is flattening nested collections.

Main.java
void main() {

    List<List<String>> nestedLists = List.of(
        List.of("a", "b", "c"),
        List.of("d", "e"),
        List.of("f", "g", "h", "i")
    );
    
    Stream<String> flattened = nestedLists.stream()
        .flatMap(List::stream);
    
    flattened.forEach(System.out::println);
}

This example takes a list of lists and flattens it into a single stream of strings. The List::stream method reference converts each inner list to a stream, and flatMap combines them.

Transforming and flattening

flatMap can both transform elements and flatten the results.

Main.java
void main() {

    List<Integer> numbers = List.of(1, 2, 3, 4);
    
    Stream<Integer> doubled = numbers.stream()
        .flatMap(n -> Stream.of(n, n * 2));
    
    doubled.forEach(System.out::println);
}

This example takes each number and maps it to a stream containing the original number and its double. The flatMap operation then combines all these streams.

Handling optional values

flatMap is useful for filtering out empty Optionals while extracting values.

Main.java
void main() {

    List<Optional<String>> options = List.of(
        
        Optional.of("apple"),
        Optional.empty(),
        Optional.of("banana"),
        Optional.empty(),
        Optional.of("cherry")
    );
    
    Stream<String> fruits = options.stream()
        .flatMap(Optional::stream);
    
    fruits.forEach(System.out::println);
}

This example filters out empty Optionals and extracts the values from present ones. The Optional::stream method converts an Optional to a stream of 0 or 1 elements.

Splitting strings into words

flatMap can be used to split strings and combine all words into one stream.

Main.java
void main() {

    List<String> sentences = List.of(
        "Hello there",
        "Java streams are powerful",
        "flatMap is useful"
    );
    
    Stream<String> words = sentences.stream()
        .flatMap(s -> Stream.of(s.split(" ")));
    
    words.forEach(System.out::println);
}

This example splits each sentence into words and combines all words from all sentences into a single stream. The lambda splits each string by spaces.

$ java Main.java
Hello
there
Java
streams
are
powerful
flatMap
is
useful

Working with nested objects

flatMap is useful for extracting nested collections from objects.

Main.java
record Order(String id, List<String> items) {
}

void main() {

    List<Order> orders = List.of(
        new Order("001", List.of("Shirt", "Pants")),
        new Order("002", List.of("Shoes", "Socks", "Hat")),
        new Order("003", List.of("Jacket"))
    );
    
    Stream<String> allItems = orders.stream()
        .flatMap(order -> order.items().stream());
    
    allItems.forEach(System.out::println);
}

This example extracts all items from all orders into a single stream. Each Order's items are converted to a stream and then flattened.

$ java Main.java
Shirt
Pants
Shoes
Socks
Hat
Jacket

Combining multiple streams

flatpMap can combine elements from multiple sources.

Main.java

void main() {

    Stream<String> stream1 = Stream.of("A", "B", "C");
    Stream<String> stream2 = Stream.of("X", "Y", "Z");
    
    Stream<Stream<String>> nested = Stream.of(stream1, stream2);
    
    Stream<String> combined = nested.flatMap(s -> s);
    
    combined.forEach(System.out::println);
}

This example combines two separate streams into one using flatMap. Note that once a stream is consumed (like in the flatMap operation), it can't be reused.

Generating Cartesian products

flatMap can be used to generate Cartesian products of collections.

Main.java
void main() {

    List<String> colors = List.of("Red", "Green", "Blue");
    List<String> sizes = List.of("S", "M", "L");
    
    Stream<String> products = colors.stream()
        .flatMap(color -> 
            sizes.stream()
                .map(size -> color + " " + size)
        );
    
    products.forEach(System.out::println);
}

This example generates all possible combinations of colors and sizes. Each color is paired with each size using nested flatMap and map operations.

$ java Main.java
Red S
Red M
Red L
Green S
Green M
Green L
Blue S
Blue M
Blue L

flatMap with primitive streams

You can use flatMapToInt, flatMapToDouble, or flatMapToLong to flatten and process collections of numbers. For example, you can convert a list of strings representing numbers into a single IntStream.

Main.java
void main() {

    List<String> numberStrings = List.of("1,2,3", "4,5", "6");

    IntStream numbers = numberStrings.stream()
        .flatMapToInt(s -> Arrays.stream(s.split(",")).mapToInt(Integer::parseInt));

    numbers.forEach(System.out::println);
}

This example splits each string by commas, parses the numbers, and flattens them into a single IntStream.

Parsing and flattening CSV data

flatMap can be used to parse a list of CSV strings, extract fields, and flatten them into a single stream for further processing.

Main.java
record User(String firstName, String lastName, int age) {
}

void main() {

    List<String> csvRows = List.of("John,Doe,30", "Jane,Smith,25",
            "Bob,Johnson,40");

    Stream<User> users = csvRows.stream()
            .map(row -> row.split(",")) // Split row
            .map(fields -> new User(fields[0], fields[1], 
                Integer.parseInt(fields[2]))); // Convert to User

    users.forEach(System.out::println);
}

The example demonstrates how to parse CSV data, where each row is split into fields, and then each field is used to create a User object. The flatMap method is not explicitly used here, but it can be applied if you want to flatten nested structures or combine multiple streams of users.

Source

Java Stream flatMap documentation

In this article we have explored the Java Stream flatMap method. It's a powerful tool for working with nested data structures, combining multiple streams, and transforming elements while flattening the results.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Java tutorials.