Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Java Streams: How to Flatten Nested Lists?

Learn how to use Java Streams to flatten a nested list into a single list. Discover the best approach for handling complex data transformations.
Illustration of Java Streams using flatMap to transform a nested list into a flat list, optimizing data processing. Illustration of Java Streams using flatMap to transform a nested list into a flat list, optimizing data processing.
  • 🖥️ flatMap() in Java Streams simplifies flattening nested lists by merging inner Streams into a single continuous Stream.
  • 🚀 Java Streams operate lazily, improving memory efficiency compared to traditional loops when processing large datasets.
  • ⚡ Parallel Streams (.parallelStream()) can enhance performance when flattening large collections.
  • 🔍 flatMap() is not limited to lists of numbers; it can extract properties from complex objects like Employee-project relationships.
  • 🛠️ Handling null values properly with Stream.empty() prevents NullPointerException during list flattening.

Java Streams: How to Flatten Nested Lists?

Java Streams provide a functional approach to manipulating collections, making data transformation both concise and efficient. One common challenge is dealing with nested lists—collections where each element is itself a list. Flattening such structures into a single list is useful when working with hierarchical data from APIs, JSON responses, or database queries. In this guide, we'll explore how Java Streams, particularly the flatMap() function, can help transform nested lists into flat lists efficiently.

Understanding Nested Lists in Java

A nested list is a list containing other lists as elements, forming a hierarchical structure. These lists are frequently encountered when dealing with:

  • Multi-dimensional data (e.g., matrices, tables)
  • Grouped records from APIs or databases
  • JSON arrays with nested structures

Here's a simple example of a nested list in Java:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

List<List<Integer>> nestedList = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5, 6),
    Arrays.asList(7, 8, 9)
);

Introduction to Java Streams

Java Streams, introduced in Java 8, enable functional-style processing of collections. A Stream is a sequence of elements supporting operations such as mapping, filtering, and reducing. The key advantage of Streams is their ability to process large datasets concisely and efficiently.

Key Stream Operations

  • map() – Transforms elements of a Stream individually.
  • flatMap() – Flattens nested structures into a single Stream.
  • filter() – Removes elements that don’t match a given condition.
  • collect() – Gathers Stream elements into a collection (List, Set, etc.).

How to Use Java Streams to Flatten a Nested List

Flattening a nested list in Java using Streams is straightforward with flatMap().

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

public class FlattenList {
    public static void main(String[] args) {
        List<List<Integer>> nestedList = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5, 6),
            Arrays.asList(7, 8, 9)
        );

        List<Integer> flatList = nestedList.stream()
            .flatMap(List::stream)
            .collect(Collectors.toList());

        System.out.println(flatList);
    }
}

Step-by-Step Execution

  1. .stream() – Converts nestedList into a Stream of Lists.
  2. .flatMap(List::stream) – Extracts each element from the inner lists and merges them into one continuous Stream.
  3. .collect(Collectors.toList()) – Converts the resulting Stream into a final flat list.

Output:

[1, 2, 3, 4, 5, 6, 7, 8, 9]

How flatMap Works Internally

The flatMap() function operates as follows:

  1. Transforms each sublist into a separate Stream of elements.
  2. Flattens all sublist Streams into a single continuous Stream.
  3. Merges all elements into a single list when collected.

This eliminates the need for nested loops, improving readability and reducing code complexity.

When to Use flatMap() for Flattening Lists

Using flatMap() is particularly helpful in scenarios like:

  • Processing hierarchical JSON data – Flatten API responses into usable structures.
  • Handling matrix-style collections – Convert 2D datasets into a single list for easier manipulation.
  • Merging multiple lists – Efficiently concatenate collections into a single Stream.

Alternative Methods for Flattening Nested Lists

Using a Traditional for-loop (Pre-Java 8)

Before Streams, developers used explicit loops to flatten lists:

List<Integer> flatList = new ArrayList<>();
for (List<Integer> sublist : nestedList) {
    flatList.addAll(sublist);
}

While effective, this approach is verbose and less readable compared to Streams.

Using Collectors Instead of flatMap

Another compact approach using Streams:

List<Integer> flatList = nestedList.stream()
    .collect(ArrayList::new, List::addAll, List::addAll);

Though flatMap() is generally more readable, this approach is useful for specific custom operations.

Performance Considerations When Using flatMap()

Efficiency Gains with Streams

  • Lazy Evaluation – Streams process elements on demand, reducing unnecessary computations.
  • Reduced Iteration Costs – No explicit nested loops, making processing faster for large sets.

When to Use Parallel Streams

For very large datasets, leveraging .parallelStream() can improve performance:

List<Integer> flatList = nestedList.parallelStream()
    .flatMap(List::stream)
    .collect(Collectors.toList());

However, parallel streams should be used cautiously as they introduce additional thread management overhead.

Best Practices for Using flatMap()

  1. Use it only when necessary – Flattening lists improves readability but can introduce minor performance overhead.
  2. Keep transformations readable – Avoid chaining too many operations inside a single Stream.
  3. Handle null values gracefully – Prevent NullPointerException with safety checks.
  4. Debug Streams effectively – Use .peek(System.out::println) for intermediate debugging.

Flattening Complex Object Structures

Beyond numbers, flatMap() is powerful for extracting properties from objects.

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

class Employee {
    private String name;
    private List<String> projects;

    public Employee(String name, List<String> projects) {
        this.name = name;
        this.projects = projects;
    }

    public List<String> getProjects() {
        return projects;
    }
}

public class FlattenObjects {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", Arrays.asList("Project A", "Project B")),
            new Employee("Bob", Arrays.asList("Project C", "Project D"))
        );

        List<String> projects = employees.stream()
            .flatMap(emp -> emp.getProjects().stream())
            .collect(Collectors.toList());

        System.out.println(projects);
    }
}

Output:

[Project A, Project B, Project C, Project D]

This approach is useful for extracting attributes such as:

  • User roles from accounts
  • Tags from articles
  • Product categories from a catalog

Common Issues and Debugging Tips

Handling NullPointerException in flatMap()

If any sublist is null, the Stream will throw an exception. To avoid this, handle null values safely:

.flatMap(list -> list == null ? Stream.empty() : list.stream())

Using .peek() for Debugging Stream Operations

To trace transformations:

nestedList.stream()
    .peek(System.out::println)  // Debug initial list
    .flatMap(List::stream)
    .peek(System.out::println)  // Debug after flattening
    .collect(Collectors.toList());

Conclusion

Flattening nested lists with Java Streams is a powerful technique that enhances code readability and efficiency. By leveraging flatMap(), developers can simplify complex data transformations while avoiding cumbersome nested loops. Whether handling simple lists of numbers or extracting values from complex object structures, Java Streams offer a clean and functional approach. Keep experimenting with different datasets and consider performance optimizations with parallel processing when needed.


Citations

  • Gosling, J., Joy, B., Steele, G., Bracha, G., & Buckley, A. (2014). The Java Language Specification, Java SE 8 Edition. Addison-Wesley.
  • Oracle Corporation. (2023). Java Platform, Standard Edition 17 API Specification. Retrieved from https://docs.oracle.com/en/java/javase/17/
  • Bloch, J. (2017). Effective Java (3rd Edition). Addison-Wesley.
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading