Sunday, January 2, 2022

Java Stream - flatMap() With Examples

flatMap() in Java brings two features; map operations in Java stream API and flattening a nested structure together.

In mapping operation in Java stream the given function is applied to all the elements of the stream. Where as flattening a structure, in simple terms, means bringing all the nested structures at the same level. If you have a data structure with many nested levels flattening will break all the nested levels and bring all the elements at the same level.

As example if you have a list of list of Strings, List<List<String>> like-

List<List<String>> aList = Arrays.asList(Arrays.asList("a", "b", "c"), 
        Arrays.asList("c", "d"), Arrays.asList("d", "e", "f"));
which gives you output as-
[[a, b, c], [c, d], [d, e, f]]

Then flattening it will bring everything to the same level and the structure you will have be like this-

[“a”, “b”, “c”, “c”, “d”, “c”, “e”, “f”].

Bringing both of these features (map operation and flattening a structure) together in a method flatMap() in Java means, function will be applied to all the elements of the stream and then it will be flatten to have a single level structure.

Java flatMap usage

If you have a stream of array, list or set, in Stream API operations like count, filter, map which work on the element of the streams such grouping of elements (using array or collection) will be considered as one element which may not be what you want.

With flatMap you can flatten that structure and then stream API methods will consider them as individual elements. Let's try to clarify it with an example.

Java flatMap() example

Let’s say there is a class called Order which has a collection of items. Now you want the count of all the items in all the orders. You also want to display all the items with in all the orders.

Order class

public class Order {
  private String orderId;
  private List<String> items;
  public String getOrderId() {
    return orderId;
  }
  public void setOrderId(String orderId) {
    this.orderId = orderId;
  }
  public List<String> getItems() {
    return items;
  }
  public void setItems(List<String> items) {
    this.items = items;
  }
}

Getting a stream containing all the items in all the orders

In the code an orderList is created with 2 orders. In first order there is a list of 3 items and in second order there is a list of 2 items.

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

public class FlatMapDemo {
  public static void main(String[] args) {
    List<Order> orderList = getOrderList();
    System.out.println("Item count with Map " + orderList.stream().map
      (order -> order.getItems().stream()).count());
    System.out.println("Item count with FlatMap " + orderList.stream().flatMap
      (order -> order.getItems().stream()).count());
    // displaying all the items
    Stream<String> item = orderList.stream().flatMap(order -> order.getItems().stream());
    item.forEach(System.out::println);
  }
    
  private static List<Order> getOrderList(){
    List<Order> orderList = new ArrayList<Order>();
    Order order = new Order();
    order.setOrderId("1");
    order.setItems(Arrays.asList("Item1", "Item2", "Item3"));
    orderList.add(order);
    order = new Order();
    order.setOrderId("2");
    order.setItems(Arrays.asList("Item3", "Item5"));
    orderList.add(order);
    return orderList;
  }
}

Output

Item count with Map 2
Item count with FlatMap 5
Item1
Item2
Item3
Item3
Item5

Here you can see when you try to get the count using map() method, each item list is considered as one element so the count is displayed as 2. When you flatten the lists using flatMap() method then only you get the correct count of all the items in all the orders. Using flatMap() you are also getting all the items which are stored as a list inside orders.

flatMap Methods for primitive data types

In Java stream API there are variants of flatMap method which work with primitive data types. These methods are-

  • flatMapToInt()- Works with int data type. Returns a new IntStream.
  • flatMapToLong()- Works with long data type. Returns a new LongStream.
  • flatMapToDouble()- Works with double data type. Returns a new DoubleStream.

flatMapToInt example

If you have 2D array and you want to flatten it and get a new IntStream you can get it like this -

int[][] numArray = {{1, 2}, {3, 4}, {5, 6}};
Stream<int[]> numStream = Stream.of(numArray);
IntStream iStream = Stream.of(numArray).flatMapToInt(n -> Arrays.stream(n));
iStream.forEach(System.out::println);

Output

1
2
3
4
5
6

In the lambda expression n -> Arrays.stream(n), n denotes one of the array with in the 2D array, so first time {1,2} will be passed as the mapped stream and its contents will be placed in the new IntStream, next time another array {3,4} will be passed and so on.

That's all for this topic Java Stream - flatMap() With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Advanced Tutorial Page


Related Topics

  1. Java Stream API Examples
  2. collect() Method And Collectors Class in Java Stream API
  3. Spliterator in Java
  4. Primitive Type Streams in Java Stream API
  5. Java Stream API Interview Questions And Answers

You may also like-

  1. Method Reference in Java
  2. @FunctionalInterface Annotation in Java
  3. Interface Default Methods in Java
  4. Difference Between ReentrantLock and Synchronized in Java
  5. Callable and Future in Java With Examples
  6. ListIterator in Java
  7. EnumSet in Java With Examples
  8. Is String Thread Safe in Java