Java 8: Enhancements to the Stream API with flatMap()

Published: December 20, 2016


Introduction

Java 8 introduced the powerful Streams API to simplify data processing tasks, making it easier to work with collections in a functional way. One of the key enhancements to the Streams API is the flatMap() method, which allows you to flatten nested data structures into a single stream.

In this tutorial, we will cover:

  • What flatMap() is and how it works.
  • How to use flatMap() to process nested data structures.
  • Real-world examples that demonstrate how flatMap() can simplify your code.

What is flatMap()?

The flatMap() method is used to transform each element of a stream into another stream and then flatten all of those streams into a single stream. Essentially, it is used when you have nested collections (e.g., lists of lists) and want to “flatten” them into a single stream of elements.

While the map() method transforms elements in a stream one-to-one (one element becomes one element), the flatMap() method transforms each element into zero or more elements, and the resulting streams are merged into one.


Basic Usage of flatMap()

Let’s look at a simple example where we have a list of lists and we want to flatten it into a single stream of items.

Example: Flattening a List of Lists

import java.util.*;
import java.util.stream.*;

public class FlatMapExample {
    public static void main(String[] args) {
        // List of Lists
        List<List<String>> listOfLists = Arrays.asList(
            Arrays.asList("Java", "Python", "C++"),
            Arrays.asList("JavaScript", "Ruby"),
            Arrays.asList("Go", "Swift", "Kotlin")
        );

        // Using flatMap to flatten the list of lists
        List<String> flattenedList = listOfLists.stream()
            .flatMap(List::stream)  // Flatten each inner list
            .collect(Collectors.toList());

        System.out.println(flattenedList);
    }
}

Explanation:

  • We have a list of lists of programming languages.
  • We use the flatMap() method to flatten these lists into a single stream.
  • The List::stream method reference is used to convert each list into a stream, and flatMap() flattens all these streams into one.

Output:

[Java, Python, C++, JavaScript, Ruby, Go, Swift, Kotlin]

Using flatMap() with Other Collections

flatMap() can also be used with other types of collections like Set or Map, and it allows you to process the elements in a similar fashion.

Example: Flattening a Map of Lists

Let’s consider a map where each key is associated with a list of values. We’ll use flatMap() to flatten this structure.

import java.util.*;
import java.util.stream.*;

public class FlatMapExample {
    public static void main(String[] args) {
        // Map with lists of values
        Map<String, List<String>> map = new HashMap<>();
        map.put("Fruit", Arrays.asList("Apple", "Banana", "Orange"));
        map.put("Vegetable", Arrays.asList("Carrot", "Potato", "Spinach"));

        // Flatten the map values using flatMap
        List<String> flattenedList = map.values().stream()
            .flatMap(List::stream)  // Flatten each list in the map values
            .collect(Collectors.toList());

        System.out.println(flattenedList);
    }
}

Explanation:

  • We have a map where each value is a list of strings (e.g., fruits and vegetables).
  • We use flatMap() to flatten these lists of values into a single stream of items.

Output:

[Apple, Banana, Orange, Carrot, Potato, Spinach]

When to Use flatMap()

flatMap() is especially useful when you need to:

  • Transform a collection of collections into a single collection.
  • Flatten nested data structures.
  • Merge multiple streams into one.

Here are some common scenarios where flatMap() can be useful:

  • Processing a list of lists, such as combining multiple user-generated lists of items.
  • Combining streams from multiple sources, like reading multiple files into a single stream.
  • Transforming complex hierarchical data into simpler, flat structures.

Chaining flatMap() with Other Stream Operations

Just like map()flatMap() can be combined with other stream operations such as filter()reduce(), or collect() to build more complex queries.

Example: Filtering and Flattening Data

Let’s say we have a list of users, where each user has a list of books they own. We want to extract the titles of all books from all users, but only the books with more than 100 pages.

import java.util.*;
import java.util.stream.*;

public class FlatMapExample {
    public static void main(String[] args) {
        // Sample User class with list of books
        class User {
            String name;
            List<Book> books;

            User(String name, List<Book> books) {
                this.name = name;
                this.books = books;
            }
        }

        class Book {
            String title;
            int pages;

            Book(String title, int pages) {
                this.title = title;
                this.pages = pages;
            }
        }

        // Sample users and books data
        List<User> users = Arrays.asList(
            new User("Alice", Arrays.asList(new Book("Java Basics", 150), new Book("Python Cookbook", 120))),
            new User("Bob", Arrays.asList(new Book("Advanced Java", 200), new Book("C Programming", 80))),
            new User("Charlie", Arrays.asList(new Book("Java in Depth", 300), new Book("Scala for the Brave", 150)))
        );

        // Flatten and filter books with more than 100 pages
        List<String> bookTitles = users.stream()
            .flatMap(user -> user.books.stream()) // Flatten the list of books
            .filter(book -> book.pages > 100)     // Filter books with more than 100 pages
            .map(book -> book.title)              // Map to book titles
            .collect(Collectors.toList());

        System.out.println(bookTitles);
    }
}

Explanation:

  • We have a list of users, and each user has a list of books.
  • We use flatMap() to flatten the list of books.
  • We then filter the books to keep only those with more than 100 pages.
  • Finally, we extract the book titles and collect them into a list.

Output:

[Java Basics, Python Cookbook, Advanced Java, Java in Depth, Scala for the Brave]

Conclusion

The flatMap() method is a powerful tool in Java 8’s Streams API, allowing you to flatten nested collections and perform complex operations on them in a functional style. It is especially useful when working with hierarchical or nested data structures, enabling you to transform them into a simpler, flat structure.

In this tutorial, we covered:

  • The basic usage of flatMap().
  • How to flatten nested collections like lists and maps.
  • How to chain flatMap() with other stream operations like filter() and map().

Next, we will look into practical examples of how to use flatMap() in real-world applications.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *