Java Stream flatMap()

The Java Stream flatMap() method is used for transforming and flattening operations on a stream. It takes a mapper function as an argument, which is applied to each element of the stream to produce a stream of new values. The flatMap() method then flattens the resulting streams into a single stream.

Syntax for flatMap()

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

Note – Before going ahead let’s understand what is flatting. Consider we have a list of lists of integers as below.

 List<List<Integer>> listOfLists = new ArrayList<>();
        listOfLists.add(Arrays.asList(1, 2, 3));
        listOfLists.add(Arrays.asList(4, 5, 6));
        listOfLists.add(Arrays.asList(7, 8, 9, 10));

After flatting

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

The flatMap() method is an intermediate operation, which means that it can be chained with other intermediate operations to perform more complex transformations on a stream.

Let’s see a few examples that show how to use the Stream flatMap() method.

Example 1 – Consider we have a list of lists of integers and we want to find the distinct elements from all the sublists. We can achieve this using flatMap() as follows.

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

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

        List<Integer> distinctElements = listOfLists.stream()
                .flatMap(List::stream) // Flattens the sublists into a single stream of integers
                .distinct() // Removes duplicates
                .collect(Collectors.toList());

        System.out.println(distinctElements);
    }
}

Output:-

[1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. We have a list of lists called listOfLists, where each sublist represents a collection of integers.
  2. We use stream() to convert the outer list into a stream of lists.
  3. We apply flatMap(List::stream) to flatten the stream of lists into a single stream of integers.
  4. We use distinct() to remove duplicate elements from the flattened stream.
  5. Finally, we collect the distinct elements into a list using collect(Collectors.toList()).

Example 2 – A list of names will get converted into a List of characters

package com.javatute.stream;

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

public class JavaStreamFlatMapExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Carol");

        // Create a stream of strings.
        Stream<String> nameStream = names.stream();

        // Map each name to a stream of its characters.
        Stream<Character> characterStream =
                nameStream.flatMap(name -> name.chars().mapToObj(c -> (char) c));

        // Collect the characters into a list.
        List<Character> characters = characterStream.collect(Collectors.toList());

        System.out.println(characters); 
    }
}

Output:-

[A, l, i, c, e, B, o, b, C, a, r, o, l]

Example 3 – How to flatten nested object list using Java Stream flatMap()

To flatten a nested list of objects in Java, we can use Java streams and the flatMap() operation to convert the nested structure into a flat list. Here’s a step-by-step guide on how to do this.

Suppose you have a class Person representing people with a nested list of Phone objects:

import java.util.List;

class Phone {
    private String number;

    public Phone(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }
}

class Person {
    private String name;
    private List<Phone> phones;

    public Person(String name, List<Phone> phones) {
        this.name = name;
        this.phones = phones;
    }

    public String getName() {
        return name;
    }

    public List<Phone> getPhones() {
        return phones;
    }
}

Now, let’s say we have a list of Person objects, and you want to flatten the nested list of phones into a single list of phone numbers. We can do it like this:

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

public class FlattenNestedList {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", Arrays.asList(new Phone("123"), new Phone("456"))),
            new Person("Bob", Arrays.asList(new Phone("789"), new Phone("101112"))),
            new Person("Charlie", Arrays.asList(new Phone("131415")))
        );

        List<String> phoneNumbers = people.stream()
            .flatMap(person -> person.getPhones().stream()) // Flatten the nested list
            .map(Phone::getNumber) // Extract phone numbers
            .collect(Collectors.toList());

        System.out.println(phoneNumbers);
    }
}

Output:-

[123, 456, 789, 101112, 131415]

In this code:

  1. We have a list of Person objects, each containing a list of Phone objects.
  2. We use flatMap() to flatten the nested list of Phone objects into a single stream of Phone objects.
  3. We then use map() to extract the phone numbers from the Phone objects.
  4. Finally, we collect the phone numbers into a list using collect(Collectors.toList()).

This demonstrates how to flatten a nested list of objects into a flat list using Java streams.

Example 4 – Java 8 Program for Flattening nested collections using Java Stream flatMap() method

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

public class FlattenNestedCollections {

    public static void main(String[] args) {
        // Create a nested list of integers.
        List<List<Integer>> nestedList = Arrays.asList(
                Arrays.asList(1, 2, 3),
                Arrays.asList(4, 5, 6),
                Arrays.asList(7, 8, 9)
        );

        // Flatten the nested list.
        Stream<Integer> flattenedList = nestedList.stream().flatMap(list -> list.stream());

        // Collect the flattened list into a new list.
        List<Integer> flattenedIntegers = flattenedList.collect(Collectors.toList());

        // Print the flattened list.
        System.out.println(flattenedIntegers); 
    }
}

Output:-

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

Java flatMap() example with Optional class

The Java Stream flatMap() method can also be used with the Optional class in Java to flatten optional values. Here’s an example of how to use flatMap() with Optional:

import java.util.Optional;

public class OptionalFlatMapExample {
    public static void main(String[] args) {
        // Create optional values
        Optional<String> stringOptional = Optional.of("Hello");
        Optional<Optional<String>> nestedOptional = Optional.of(Optional.of("World"));

        // Using flatMap to flatten the nested optional
        Optional<String> result = stringOptional.flatMap(str -> nestedOptional);

        // Printing the result
        if (result.isPresent()) {
            System.out.println(result.get()); // Output: World
        } else {
            System.out.println("Result is empty.");
        }
    }
}

Output:-

World

Using flatMap() in this way allows you to flatten nested Optional instances and work with the inner value without dealing with multiple levels of nesting.

Example 6 – How to avoid infinite loops with flatMap()

In Java, using flatMap() can lead to infinite loops, especially if we are dealing with streams of infinite data sources. Infinite loops can occur when the function passed to flatMap() generates new elements indefinitely, causing the stream to never terminate.

To avoid infinite loops with flatMap(), we should ensure that the transformation function applied to each element in the stream does not generate an infinite number of elements. Here are some strategies to prevent infinite loops:

  1. Limit the Number of Elements Generated: Use methods like limit() to restrict the number of elements produced by the transformation function. For example:
   List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

   List<Integer> result = numbers.stream()
       .flatMap(n -> Stream.iterate(n, i -> i + 1).limit(3)) // Limit the number of generated elements
       .collect(Collectors.toList());

   System.out.println(result);

In this example, limit(3) ensures that only three elements are generated for each input element.

  1. Use Streams with Finite Data Sources: If you’re working with infinite data sources, make sure to use streams with finite sources or apply operations that eventually terminate the stream. For example:
   Stream<Integer> numbers = Stream.iterate(1, i -> i + 1).limit(10); // Generate a finite stream

   List<Integer> result = numbers
       .flatMap(n -> Stream.iterate(n, i -> i + 1)) // Apply a terminating operation
       .limit(20) // Limit the total number of elements in the final stream
       .collect(Collectors.toList());

   System.out.println(result);

Here, the limit(20) ensures that the final stream is limited to a specific number of elements.

  1. Use Appropriate Data Structures: Ensure that the data structures you’re working with can handle the potential size of the output. Avoid using data structures that can grow indefinitely without bounds, as this can lead to memory issues.
  2. Check Your Transformation Functions: Be cautious with custom transformation functions passed to flatMap(). Ensure that they are designed to produce a finite number of elements.

By applying these strategies and being mindful of the functions you use within flatMap(), you can avoid infinite loops and ensure that your stream processing terminates appropriately.

Here are ten objective questions related to Java 8 Stream flatMap() method along with four options for each question. The answers are provided at the end.

Question 1: What is the purpose of the flatMap() method in Java 8 Streams?

A) To transform each element in a stream to another element.
B) To filter elements in a stream based on a given predicate.
C) To flatten a stream of collections or arrays into a single stream.
D) To aggregate elements in a stream into a single result.

Question 2: Which of the following statements about flatMap() is correct?

A) flatMap() always returns a stream of the same type as the original stream.
B) flatMap() can be used to transform elements and collect them into a list.
C) flatMap() is used to filter elements in a stream.
D) flatMap() can only be applied to finite streams.

Question 3: In a stream of Optional objects, how can flatMap() be used to extract the values from the optionals?

A) flatMap(optional -> optional)
B) flatMap(Optional::get)
C) flatMap(Optional::of)
D) flatMap(Optional::isEmpty)

Question 4: When using flatMap(), what does the mapping function return?

A) A stream of elements
B) A single element
C) A boolean value
D) A list of elements

Question 5: What happens if the mapping function in flatMap() returns null for an element in the stream?

A) It throws a NullPointerException.
B) It filters out the element from the resulting stream.
C) It includes the null value in the resulting stream.
D) It terminates the stream immediately.

Question 6: How can you limit the number of elements produced by flatMap() for each element in the stream?

A) Use filter() to limit the elements.
B) Use distinct() to remove duplicates.
C) Use limit() after flatMap() to limit the elements.
D) Use flatMap() with a custom collector.

Question 7: Which method is used to extract elements from the inner collections when using flatMap()?

A) extract()
B) stream()
C) flatten()
D) collect()

Question 8: In the context of flatMap(), what is the purpose of flattening nested data structures?

A) To increase memory usage.
B) To improve performance.
C) To simplify data processing.
D) To create complex data structures.

Question 9: When using flatMap(), what is the expected output of flattening a stream of arrays?

A) A stream of arrays.
B) A single array.
C) A stream of elements.
D) A stream of stream of elements.

Question 10: In Java 8, which package is the Stream class located in?

A) java.util.stream
B) java.stream
C) java.collections
D) java.streams

Answers:

  1. C) To flatten a stream of collections or arrays into a single stream.
  2. A) flatMap() always returns a stream of the same type as the original stream.
  3. B) flatMap(Optional::get)
  4. A) A stream of elements
  5. B) It filters out the element from the resulting stream.
  6. C) Use limit() after flatMap() to limit the elements.
  7. B) stream()
  8. C) To simplify data processing.
  9. C) A stream of elements.
  10. A) java.util.stream

Java Stream flatMethod() docs.

That’s all about Java Stream flatMap() method.