Question
Given a List<List<Object>>, how can you create a single List<Object> containing every object from the nested lists in the same encounter order by using Java 8 features?
Short Answer
You will learn how Java 8 streams transform nested collections into one flat collection with flatMap, while preserving the order of elements. You will also see alternatives, null-handling options, and common pitfalls.
Concept
A List<List<Object>> is a nested collection: its outer list contains inner lists rather than individual objects.
To create one list containing the objects from every inner list, you need to flatten the structure. In Java 8 Streams, flatMap is designed for this job.
maptransforms each input item into exactly one output item.flatMaptransforms each input item into a stream of output items, then joins those streams into one stream.
For nested lists, each inner List<Object> can become a Stream<Object>. flatMap combines those streams, and collect(Collectors.toList()) stores the resulting objects in a new list.
This matters because nested data appears often in real programs: orders containing line items, teams containing members, API responses containing grouped results, and batches containing records.
Mental Model
Imagine the outer list as a stack of trays. Each tray is an inner list, and each tray holds objects.
mapwould replace every tray with something else, but you would still have one result per tray.flatMapremoves the tray boundaries and pours all objects onto one long conveyor belt.
The conveyor belt is the final stream of objects, which you collect into a single List<Object>.
Syntax and Examples
The usual Java 8 pattern is:
List<T> flattened = nestedLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Example:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<List<String>> wordsByGroup = Arrays.asList(
Arrays.asList("red", "blue"),
Arrays.asList("green"),
Arrays.asList("yellow", "purple")
);
List<String> words = wordsByGroup.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(words);
}
}
Output:
[red, blue, green, yellow, purple]
wordsByGroup.stream() produces a stream of inner lists. flatMap(List::stream) turns each inner list into a stream of strings and merges them into one stream. Finally, collect(...) creates the new List<String>.
Step by Step Execution
Consider this code:
List<List<Integer>> batches = Arrays.asList(
Arrays.asList(10, 20),
Arrays.asList(30),
Arrays.asList(40, 50)
);
List<Integer> values = batches.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Execution trace:
batches.stream()visits the outer list in order.- The first inner list is
[10, 20].List::streamproduces10, then20. - The second inner list is
[30]. It produces30. - The third inner list is
[40, 50]. It produces40, then50. flatMapcombines those element streams into one sequence:10, 20, 30, 40, 50.collect(Collectors.toList())createsvaluesas[10, 20, 30, 40, 50].
Real World Use Cases
Common flattening scenarios include:
- E-commerce: Convert
List<Order>into one list of all order line items. - User permissions: Combine permission lists from several roles before checking access.
- API processing: Flatten paginated or grouped API results into one result list.
- File processing: Combine lists of parsed records from multiple input files.
- Validation: Collect all validation errors from several form sections.
- Data import: Flatten batches of database rows before filtering or transforming them.
Real Codebase Usage
In production code, flattening is often followed by another stream operation.
Flatten and filter
List<String> activeUsernames = usersByTeam.stream()
.flatMap(List::stream)
.filter(User::isActive)
.map(User::getUsername)
.collect(Collectors.toList());
Flatten and remove duplicates
List<String> uniqueTags = tagsByArticle.stream()
.flatMap(List::stream)
.distinct()
.collect(Collectors.toList());
Ignore null inner lists
If an outer list might contain null instead of an inner list, filter those values before calling stream():
List<String> names = groups.stream()
.filter(Objects::nonNull)
.flatMap(List::stream)
.collect(Collectors.toList());
A common design preference is to avoid null collections altogether. Return an empty list when there are no items, because an empty list works naturally with flatMap.
Common Mistakes
Using map instead of flatMap
This code does not flatten the lists:
List<Stream<Object>> streams = nestedLists.stream()
.map(List::stream)
.collect(Collectors.toList());
map(List::stream) creates one Stream<Object> per inner list. The result still has one item per inner list. Use flatMap(List::stream) to merge those streams.
Forgetting to collect the stream
nestedLists.stream().flatMap(List::stream);
Streams are lazy. Without a terminal operation such as collect, forEach, or count, no useful final result is produced.
Calling stream() on a null inner list
nestedLists.stream()
.flatMap(List::stream) // Fails if an inner list is null
.collect(Collectors.toList());
Filter null inner lists first, or ensure your program uses empty lists instead of null.
Assuming any collection preserves order
A preserves insertion order, so flattening a sequentially gives predictable order. A , especially a , may not have a predictable iteration order.
Comparisons
| Approach | Result | When to use it |
|---|---|---|
flatMap(List::stream) | One stream containing all nested elements | Standard Java 8 flattening approach |
map(List::stream) | A stream of streams | Use only when you intentionally need separate streams |
Nested for loops | One list after manually adding items | Useful when stream code would be less readable or logic is complex |
addAll in a loop | One list after copying each inner list | A simple non-stream alternative |
A loop-based alternative is also valid:
List<Object> result = new ArrayList<>();
for (List<Object> innerList : nestedLists) {
result.addAll(innerList);
}
For a direct flattening operation, the stream version is concise. For complicated branching, logging, checked exceptions, or mutation, a loop can be easier to read and debug.
Cheat Sheet
// Flatten a List<List<T>> into List<T>
List<T> result = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// Equivalent lambda form
.flatMap(innerList -> innerList.stream())
// Safely ignore null inner lists
nested.stream()
.filter(Objects::nonNull)
.flatMap(List::stream)
.collect(Collectors.toList());
Key rules:
- Use
flatMapto remove one level of nesting. - Use
mapwhen each input should produce one output. collect(Collectors.toList())turns a stream into a list.- Sequential streams over ordered lists preserve encounter order.
- Prefer empty lists over null lists when possible.
FAQ
What does flatMap(List::stream) mean in Java?
It converts each inner list into a stream and merges all of those element streams into one stream.
Does flatMap preserve the order of a List<List<T>>?
Yes, for a sequential stream over normal ordered lists. Elements are visited in outer-list order and then inner-list order.
Can I use a lambda instead of List::stream?
Yes. These are equivalent:
.flatMap(List::stream)
.flatMap(list -> list.stream())
What happens if an inner list is empty?
It contributes no elements. flatMap simply continues with the next inner list.
What happens if an inner list is null?
Calling stream() on it throws a NullPointerException. Filter null values or use empty lists instead.
Can I flatten a list of lists into a set?
Yes. Replace toList() with toSet() when you want unique elements and do not require list behavior:
.collect(Collectors.toSet())
Does modify the original lists?
Mini Project
Description
Build a small order-item report. Each order contains a list of purchased product names. Flatten all orders into one list, remove duplicate product names, and print the result. This mirrors reporting and inventory tasks that combine nested records.
Goal
Create one alphabetically sorted list of unique products from nested order data using Java 8 streams.
Requirements
- Create a
List<List<String>>containing at least three orders. - Flatten all product lists into one stream.
- Ignore any empty order lists naturally.
- Remove duplicate product names.
- Sort the final product list alphabetically.
- Print the final list.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.