DevNotes
Support
Java 8 & Streams · Chapter 10

flatMap()

The most misunderstood Stream operation — flattening nested collections, map() vs flatMap(), chained flatMap, flatMapToInt and null-safe flattening.

◷ 7 min read§ 16 sections? 4 interview questions{ } 26 code examples

#Introduction

This topic confuses almost every Java developer at first. A common pattern in interviews: candidates know map(), but don't really understand flatMap().

Once you understand flatMap(), you'll solve problems involving nested lists, Orders → Items, Employee → Skills, Customer → Orders, Department → Employees, Files → Lines, JSON arrays and database relationships. A must-know topic for Spring Boot developers.

By the end of this chapter you'll master what flatMap() actually does, the difference between map() and flatMap(), flattening nested collections, flatMapToInt(), and advanced interview problems.

#First, Understand map()

Java
List<String> names = Arrays.asList("John", "Alex", "David");

names.stream()
     .map(String::toUpperCase)
Output
JOHN
ALEX
DAVID

One input → one output. Every element becomes exactly one element.

Now Imagine This

Instead of John, suppose every employee has skills: John → Java, SQL, Spring.

Now one employee becomes multiple values. map() cannot flatten this — this is where flatMap() comes in.

#What is flatMap()?

Employee
Stream<Skill>
One flat stream

#The Classic Example

Java
List<List<Integer>> list = Arrays.asList(
        Arrays.asList(1, 2),
        Arrays.asList(3, 4),
        Arrays.asList(5, 6)
);
Text
[
  [1, 2],
  [3, 4],
  [5, 6]
]

Using map()

Java
list.stream()
    .map(List::stream)

Result: Stream<Stream<Integer>> — a stream of streams of numbers. Not useful.

Using flatMap()

Java
list.stream()
    .flatMap(List::stream)
    .forEach(System.out::println);
Output
1
2
3
4
5
6
[1, 2] · [3, 4] · [5, 6]
1 2 3 4 5 6one stream

Why "Flat"?

List<List<Integer>>nested
Stream<Integer>flat

#Employee Skills Example

Java
class Employee {
    String name;
    List<String> skills;
}

Data: John → Java, Spring, SQL and Alex → Docker, AWS. Requirement: get all skills.

Java
employees.stream()
         .flatMap(e -> e.getSkills().stream())
         .forEach(System.out::println);
Output
Java
Spring
SQL
Docker
AWS

Remove Duplicate Skills

Java
employees.stream()
         .flatMap(e -> e.getSkills().stream())
         .distinct()
         .forEach(System.out::println);

No duplicates — if two employees both know Java, it's printed once.

Count Skills

Java
long count = employees.stream()
        .flatMap(e -> e.getSkills().stream())
        .count();

Sort Skills

Java
employees.stream()
         .flatMap(e -> e.getSkills().stream())
         .sorted()
         .forEach(System.out::println);

flatMap + filter

Need only Java-related skills:

Java
employees.stream()
         .flatMap(e -> e.getSkills().stream())
         .filter(skill -> skill.startsWith("Java"))
         .forEach(System.out::println);

#Orders Example

Java
class Order {
    List<Item> items;
}

Need all items:

Java
orders.stream()
      .flatMap(order -> order.getItems().stream())
      .collect(Collectors.toList());

A very common Spring Boot pattern.

#Customer → Orders → Items

Customer
Orders
Items

Need all items:

Java
customers.stream()
         .flatMap(customer -> customer.getOrders().stream())
         .flatMap(order -> order.getItems().stream())
         .collect(Collectors.toList());

Notice the multiple flatMap() calls.

#File Processing

File
Lines
Words
Java
try (Stream<String> lines = Files.lines(path)) {
    lines.flatMap(line -> Arrays.stream(line.split(" ")))
         .forEach(System.out::println);
}

Output: every word in the file. Very useful.

#flatMapToInt()

Java
class Student {
    List<Integer> marks;
}

Need total marks:

Java
int total = students.stream()
        .flatMapToInt(s -> s.getMarks().stream().mapToInt(Integer::intValue))
        .sum();

#map() vs flatMap()

Asked constantly.

map()flatMap()
One → OneOne → Many (zero or more)
Returns a StreamReturns a flattened Stream
No flatteningFlattens nested streams
Employee → Name: map(Employee::getName)Employee → Skills: flatMap(e -> e.getSkills().stream())

#Real Spring Boot Examples

Java
// Employee -> skills
employees.stream()
         .flatMap(e -> e.getSkills().stream());

// Orders -> items
orders.stream()
      .flatMap(o -> o.getItems().stream());

// Department -> employees
departments.stream()
           .flatMap(d -> d.getEmployees().stream());

// Roles -> permissions
roles.stream()
     .flatMap(r -> r.getPermissions().stream());

JWT Roles

Suppose a JWT contains ROLE_ADMIN, ROLE_USER and ROLE_MANAGER, and each role maps to a set of permissions:

Java
claims.getRoles()
      .stream()
      .flatMap(role -> role.getPermissions().stream())

Very common in Spring Security.

#Interview Questions

Q1Difference between map() and flatMap()?

map() transforms one element into one element. flatMap() transforms one element into multiple (zero or more) elements and flattens them.

Q2Why not use map(List::stream)?

Because it returns Stream<Stream<T>> instead of Stream<T>.

Q3When do you use flatMap()?

Whenever an object contains a collection — Employee → Skills, Order → Items, Customer → Orders.

Q4Can we chain flatMap()?

Absolutely. Company → Departments → Employees → Skills:

Java
company.stream()
       .flatMap(...)
       .flatMap(...)
       .flatMap(...)

#Practice Problems

Java
class Employee {
    String name;
    List<String> skills;
}
  1. Print all skills.
  2. Print unique skills.
  3. Sort all skills.
  4. Count skills.
  5. Print only skills starting with "J".
  6. Convert skills into a Set.
  7. Join all skills with commas.
  8. Find the longest skill name.
  9. Count unique skills.
  10. Find the alphabetically first skill.
Java
class Customer {
    List<Order> orders;
}

class Order {
    List<Item> items;
}
  1. Get all orders.
  2. Get all items.
  3. Count all items.
  4. Find unique items.
  5. Find the most expensive item.
  6. Find the cheapest item.
  7. Group items by category.
  8. Count items by category.
  9. Sort items by price.
  10. Find the top 5 expensive items.

Senior Interview Challenge (5+ Years)

Java
class Company {
    List<Department> departments;
}

class Department {
    String name;
    List<Employee> employees;
}

class Employee {
    String name;
    List<String> skills;
}

Return a Map<String, Set<String>> where key = department name and value = unique, sorted employee skills.

Java
Map<String, Set<String>> result = company.getDepartments().stream()
        .collect(Collectors.toMap(
                Department::getName,
                department -> department.getEmployees().stream()
                        .flatMap(employee -> employee.getSkills().stream())
                        .collect(Collectors.toCollection(TreeSet::new))
        ));

Why this is a great interview solution:

  • toMap() creates the department-to-skills mapping.
  • flatMap() flattens all employee skill lists into one stream.
  • TreeSet both removes duplicates and keeps the skills sorted.

#Senior Java Tips

1. The Golden Rule

Ask yourself: does each input produce exactly one output?

One output per input?
map()
flatMap()

2. Don't confuse flatMap() with Collectors.flatMapping()

flatMap()Collectors.flatMapping()
Stream operationCollector (Java 9+)
Before collect()Inside groupingBy()
Java
Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.flatMapping(e -> e.getSkills().stream(), Collectors.toSet())
)

3. Watch for null

This can throw a NullPointerException:

Java
.flatMap(e -> e.getSkills().stream())

If getSkills() might return null, use:

Java
.flatMap(e -> Optional.ofNullable(e.getSkills())
        .orElse(Collections.emptyList())
        .stream())

This makes your pipeline null-safe.

#You've Completed the Core of Java Streams

You now understand the stream lifecycle, filter(), map(), sorted(), distinct(), limit(), skip(), reduce(), collect(), groupingBy(), partitioningBy(), advanced collectors and flatMap() — the APIs you'll use in the majority of enterprise Java codebases. From here on, the focus shifts from learning APIs to solving problems.

#Chapter Summary

  • flatMap() = map each element to a stream, then flatten
  • map(List::stream) gives Stream<Stream<T>>
  • Chained flatMap() for deep nesting
  • File → lines → words
  • flatMapToInt()
  • flatMap() vs Collectors.flatMapping()
  • Null-safe flattening