Tuesday, April 14, 2020

Java 8 flatMap Examples - Stream flatmap convert Stream<List<List<String>>> into Stream<String>

1. Introduction


In this article, We'll learn about java 8 new Stream API flatMap() method. When to use it and how to use it.

flatMap() method is used to convert or flatten Stream of collections into Stream of collection values by removing the collection.

Removing collection such as List or Set from Stream is called flattening.

FlatMap() is part of Stream Intermediate Operations in Java 8.

Java 8, Stream can hold any type of collections and can be converted into Stream<T> as below.

Stream<List<List<String>>> --> apply flatMap() logic --> Stream<String>
Stream<Set<Set<String>>> --> apply flatMap() logic --> Stream<String>
Stream<List<String>>> --> apply flatMap() logic --> Stream<String>
Stream<List<Object>>> --> apply flatMap() logic --> Stream<Object>


2. What is Flattening?


Flattening means converting multiple lists of lists and merge all values from several lists into a single list. In the end, we'll have only one List consisting of all values.

For Example,

Code Box
[Before Flattening: [ [{1, 2, 3}, {4, 5, 6}], [{7, 8}, {9, 10}], [11, 12, 13]] After Flattening : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]

3. Stream flatMap() Syntax

The below is the syntax from Stream API.

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

This method takes Function FI(Functional Interface) as a parameter. Function has apply() method that takes one argument and returns a value.

Finally, flatMap() returns a Stream of Function FI output type. eg. Stream <String>, Stream<Employee>. Always, It creates a new Stream as Output.

This method has different versions for primitive types as below. All will be shown with examples in the next articles.

flatMapToInt(Function f) : returns IntStream
flatMapToLong(Function f) : returns LongStream
flatMapToDouble(Function f) : returns DoubleStream

4. flatMap() Characteristics


The following are applicable to flatMap() when the function is executed.


  • It is an intermediate operation and returns a new stream with all the values.
  • Returns a stream consisting of the results of replacing each element of the given stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
  • The function used for transformation in flatMap() is a stateless function and returns only a stream of new values.
  • Each mapped stream is closed after its contents have been placed into a new stream.
  • flatMap() operation flattens the stream; opposite to map() operation which does not apply to flatten


5. Stream flatMap() Example: Converting List of List into a single List


In the below examples, Created a List of lists as List<List<String>> and it converts into List<String> with all values of all lists.

Just call flatMap() method to convert List<List<String>> to List<String>.

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

/**
 * 
 * List<List<String>> to List<String>
 * 
 * @author Java8Example
 *
 */

public class Java8StreamFlatMapExample1 {

 public static void main(String[] args) {

  List<String> list1 = Arrays.asList("One", "Two", "Three");
  List<String> list2 = Arrays.asList("Four", "Five", "Six", "Seven");
  List<String> list3 = Arrays.asList("Eight", "Nine");

  List<List<String>> listOfLists = Arrays.asList(list1, list2, list3);

  System.out.println("List of Lists before flattening (List<List<String>>) : " + listOfLists);
  System.out.println("List of Lists before flattening size : " + listOfLists.size());

  Stream<String> flatmapStream = listOfLists.stream().flatMap(list -> list.stream());

  List<String> output = flatmapStream.collect(Collectors.toList());

  System.out.println("After Flattening flatMap() output " + output);
  System.out.println("After Flattening flatMap() output size " + output.size());

 }

}

Output:

List of Lists before flattening (List<List<String>>) : [[One, Two, Three], [Four, Five, Six, Seven], [Eight, Nine]]
List of Lists before flattening size : 3

After Flattening flatMap() output [One, Two, Three, Four, Five, Six, Seven, Eight, Nine]
After Flattening flatMap() output size 9

6. Stream flatMap() Example: Converting Arrays of Arrays integers into a single Array


Exampel program to covnert Integer[][] into List<Integers>.

Integer[][] is having nested arrays. This is the simple way of collecting all values into a single list instead of iterating all over the Integer[][] array.

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

/**
 * 
 * Integer[][] to List<Integer>
 * 
 * @author Java8Example
 *
 */

public class Java8StreamFlatMapExampleArrays2 {

 public static void main(String[] args) {

  // Create array.
  Integer[][] intArray = new Integer[][] { { 1, 2, 3 }, { 4, 5 }, { 6, 7, 8, 9 } };

  // converting array to Stream
  Stream<Integer[]> stream = Arrays.stream(intArray);

  // stream<Integer> using flatMap()
  Stream<Integer> intStream = stream.flatMap(array -> Arrays.stream(array));

  List<Integer> output = intStream.collect(Collectors.toList());

  System.out.println("After Flattening flatMap() output " + output);

 }

}

Output:

After Flattening flatMap() output [1, 2, 3, 4, 5, 6, 7, 8, 9]

7. Stream + flatMap() -> List


If you want to do any comparisons or validations using filter() method then you should use flatMap() first then apply filter() method.

If you do not use flatMap() then it will find the match to the given filter(Predicate p).

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

/**
 * 
 * String[][] to String<Integer>
 * 
 * @author Java8Example
 *
 */

public class Java8StreamFlatMapExampleArraysString {

 public static void main(String[] args) {

  // Create array.
  String[][] strArray = new String[][] { { "100", "200", "300" }, { "400", "500" },
    { "600", "700", "800", "900" } };

  // converting array to Stream
  Stream<String[]> strStream = Arrays.stream(strArray);

  // stream<String> using flatMap()
  Stream<String> flatMapStream = strStream.flatMap(array -> Arrays.stream(array));

  // filtering based on a condition.
  Stream<String> filteredStream = flatMapStream.filter(value -> value.endsWith("00"));

  List<String> output = filteredStream.collect(Collectors.toList());

  System.out.println("After Flattening flatMap() output " + output);

 }

}

Output: 

After Flattening flatMap() output [100, 200, 300, 400, 500, 600, 700, 800, 900]

8. Stream>> + flatMap() -> Set


Let us filter only Employee designation by "Developer" using flatmap() from all the employees of Apple and Samsung.

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.java.w3schools.blog.java8.stream.Employee;

/**
 * 
 * String[][] to String<Integer>
 * 
 * @author Java8Example
 *
 */

public class Java8StreamFlatMapSetEmployee {

 public static void main(String[] args) {

  Employee appleEmp1 = new Employee(100, "Jira Sunait", "Developer", "Apple");
  Employee appleEmp2 = new Employee(200, "Suman Jaran", "Lead", "Apple");
  Employee appleEmp3 = new Employee(300, "Riyad Paul", "Developer", "Apple");

  Set<Employee> appleSet = new HashSet<>();

  appleSet.add(appleEmp1);
  appleSet.add(appleEmp2);
  appleSet.add(appleEmp3);

  Employee samsungEmp1 = new Employee(400, "Baiym Chpal", "Developer", "Samsung");
  Employee samsungEmp2 = new Employee(500, "Atthat Gates", "Lead", "Samsung");

  Set<Employee> samsungSet = new HashSet<>();

  appleSet.add(samsungEmp1);
  appleSet.add(samsungEmp2);

  Set<Set<Employee>> allEmps = new HashSet<>();

  allEmps.add(appleSet);
  allEmps.add(samsungSet);

  Set<Employee> developers = allEmps.stream().flatMap(set -> set.stream())
    .filter(emp -> emp.getDesgnation().equals("Developer")).collect(Collectors.toSet());

  System.out.println("Filterting only developers from all companies as Set : " + developers);

 }

}

Output:

Filterting only developers from all companies as Set : 
[Employee [id=300, fullName=Riyad Paul, age=0, desgnation=Developer, company=Apple],
 Employee [id=400, fullName=Baiym Chpal, age=0, desgnation=Developer, company=Samsung],
  Employee [id=100, fullName=Jira Sunait, age=0, desgnation=Developer, company=Apple]]

9. Stream>> + flatMap() -> List

Collecting the employees desingnation Developer as List.

List<Employee> developerslist = allEmps.stream().flatMap(set -> set.stream())
    .filter(emp -> emp.getDesgnation().equals("Developer")).collect(Collectors.toList());

System.out.println("Filterting only developers from all companies as List : " + developerslist);

This code produces output as the above program.


10. Stream IllegalStateException 


if the Stream is closed and invoked any options on it then it will throw "java.lang.IllegalStateException: stream has already been operated upon or closed". This is a common exception in java 8 streams.

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
 at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
 at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
 at com.java.w3schools.blog.java.program.to.java8.stream.Java8StreamFlatMapExampleArraysString.main(Java8StreamFlatMapExampleArraysString.java:33)

11. Conclusion


In this article, we've seen many examples of the Stream API flatMap() method.

As usual, Examples are shown in this article are over GitHub.

GitHub Code

Stream filter Example

No comments:

Post a Comment