flatmap in Java 8 streams
-
Last Updated: March 10, 2024
-
By: javahandson
-
Series
In this article, we will try to understand what is a flatmap in Java 8 streams with examples. If you want to learn more about other Java, Java 8, or Spring concepts then please click the above menu options.
The flatmap is used to flatten down the streams. Suppose if there are multiple streams then we can use flatmap to flatten down those multiple streams into a single stream. We will try to understand this step by step.
Write a program to print distinct characters from a list of characters.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class UniqueElements { public static void main(String[] args) { List<Character> list = Arrays.asList('a', 'f', 'a', 'c', 'f', 'b'); List<Character> distinctElements = list.stream() .distinct() .collect(Collectors.toList()); System.out.println("Distinct elements : "+distinctElements); } } Output: Distinct elements : [a, f, c, b]
In the above program ‘a’ and ‘f’ were repeated but in the output, we were successfully able to print only unique elements using the distinct method.
Now say if we have a list of words and we have to print the unique characters from those words. For ex. From this given list of words {“Learning”, “Java”} we would like to return a list of unique elements like below
{“L”, “e”, “a”, “r”, “n”, “i”, “g”, “J”, “v”}
You might think that this is easy, we can map each word into a list of characters, and then using a distinct method we can get a new list of unique elements. Let us try it.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class UniqueElementsFromWords { public static void main(String[] args) { List<String> list = Arrays.asList("Learning", "Java"); List<String[]> newList = list.stream() .map(word -> word.split("")) .distinct() .collect(Collectors.toList()); System.out.println("New List is : "+ newList); for (int i=0; i<newList.size(); i++) { for (String element : newList.get(i) ) { System.out.print(element + " "); } System.out.println(); } } } Output: New List is : [[Ljava.lang.String;@8db2f2, [Ljava.lang.String;@18bf509] L e a r n i n g J a v a
Are we successful in achieving our desired outcome? No, we are not. The map method returns a Stream for each word
{{“L”, “e”, “a”, “r”, “n”, “i”, “n”, “g”}, {“J”, “a”, “v”, “a”}}
and distinct method was applied on String[] hence it didn’t work.
Instead of Stream<String[]> what we actually want was Stream<String> to represent a stream of characters something like below
{“L”, “e”, “a”, “r”, “n”, “i”, “n”, “g”, “J”, “a”, “v”, “a”}
and then we should have applied a distinct function on the above Stream so that we could get the unique characters. Not to be worried we can achieve this using a flatmap method.
The Stream interface has a flatmap method that returns a single stream. This single stream is a result of replacing each element of the multiple streams with the contents of a mapped stream that is produced by applying the mapping function to each element.
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper) Type Parameters: R - The element type of the new stream Parameters: mapper - It is a stateless function that is applied to each element which produces a stream of new values Returns: A new transformed stream
Write a program to print the unique characters from a list of words.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class UniqueElementsFromWords { public static void main(String[] args) { List<String> list = Arrays.asList("Learning", "Java"); Stream<String[]> streamStringArray = list.stream() .map(word -> word.split("")); Stream<String> streamString = streamStringArray .flatMap(Arrays::stream); List<String> uniqueString = streamString.distinct() .collect(Collectors.toList()); System.out.print(uniqueString); } } Output: [L, e, a, r, n, i, g, J, v]
a. We have taken a list of words {“Learning”, “Java”}
b. We are mapping each word to a separate String[] and this map function returns a Stream<String[]>.
{{“L”, “e”, “a”, “r”, “n”, “i”, “n”, “g”}, {“J”, “a”, “v”, “a”}}
c. Now we are converting each String[] to a Stream using Arrays::stream and then we are flattening these multiple streams into a single stream using the flatMap method.
{“L”, “e”, “a”, “r”, “n”, “i”, “n”, “g”, “J”, “a”, “v”, “a”}
d. Now we are applying the distinct method that filters out the unique elements and collecting them into a list.
[L, e, a, r, n, i, g, J, v]
So this is how we are able to print the unique characters from a list of words. I hope you got a good understanding of the flatmap method. In the below section, we will try to solidify the understanding of flatMap using a different example.
We can map 2D structures such as a 2D array or 2 levels of the stream to a 1D array or 1 level of the stream using a flatMap. The below table will give you a better picture.
Input | Operation | Output |
String [ ] [ ] | flatMap | String [ ] |
Stream < String [ ] > | flatMap | Stream < String > |
Stream < Stream < String > > | flatMap | Stream < String > |
Stream < List < String > > | flatMap | Stream < String > |
Example: Below is a 2D structure. Using flatMap we can convert it to a 1D structure.
{ {"Hello", "we"}, {"are", "learning"}, {"flatMap", "method"} } After applying flatMap the above 2D array will be converted to 1D array like below {"Hello", "we", "are", "learning", "flatMap", "method"}
Write a program to convert a 2D array of words to a 1D array of words using flatMap.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class ConvertTo1DStructure { public static void main(String[] args) { String[][] words = { {"Hello", "we"}, {"are", "learning"}, {"flatMap", "method"} }; System.out.println("2D array : "+Arrays.deepToString(words)); Stream<String[]> streamOfWords = Arrays.stream(words); Stream<String> singleStreamOfWords = streamOfWords.flatMap(Arrays::stream); List<String> list = singleStreamOfWords.collect(Collectors.toList()); System.out.print("1D array : "+list); } } Output: 2D array : [[Hello, we], [are, learning], [flatMap, method]] 1D array : [Hello, we, are, learning, flatMap, method]
flatMap method has different variants just like the map method that we have learned in the previous article. Below are different types of flatMap methods.
The Stream interface has a flatMapToInt method that returns an IntStream using a mapping. IntStream is a sequence of primitive integer value elements that supports sequential and parallel aggregate operations. function.
IntStream flatMapToInt(Function<? super T,? extends IntStream> mapper) Parameters: mapper - It is a stateless function that will be applied to each element. Returns: A new transformed stream that contains the integer elements
Write a program to get the length of words in a 2D array using the flatMapToInt method.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; public class FlatMapToIntExample { public static void main(String[] args) { String[][] words = { {"Hello", "we"}, {"are", "learning"}, {"flatMap", "method"} }; Stream<String[]> streamOfWords = Arrays.stream(words); IntStream intStream = streamOfWords.flatMapToInt(wordsArray -> Arrays.stream(wordsArray).mapToInt(word -> word.length())); List<Integer> lengthOfWords = intStream.boxed().collect(Collectors.toList()); // We are just getting the listOfWords for printing the words in the subsequent for loop List<String> listOfWords = Arrays.stream(words).flatMap(Arrays::stream).collect(Collectors.toList()); for (int i=0; i<listOfWords.size(); i++) { System.out.println("Length of " + listOfWords.get(i) + " is = " + lengthOfWords.get(i)); } } } Output: Length of Hello is = 5 Length of we is = 2 Length of are is = 3 Length of learning is = 8 Length of flatMap is = 7 Length of method is = 6
a. We are given a 2D array of words.
b. We are converting the “words” array to Stream using the Arrays.stream method.
c. We are calling a flatMapToInt method on the above Stream.
d. The input to the flatMapToInt method is a String[], if we trigger a length method on the String[] then we will not get the length of individual words instead we will get the length of a string array. Hence firstly we are converting the Stream<String[]> to Stream<String> using the Arrays.stream method and then using the mapToInt method we are mapping the word to its length. The return type of the flatMapToInt is an IntStream.
e. We are converting the IntStream to a List of type Integer.
f. We are converting the 2D array of words to a single-dimensional array of words ( this step is just for printing the output )
g. Now we have the words and their length hence we are printing them in the last step.
The Stream interface has a flatMapToLong method that returns a LongStream using a mapping function. The LongStream is a sequence of primitive long-value elements that supports sequential and parallel aggregate operations.
LongStream flatMapToLong(Function<? super T,? extends LongStream> mapper) Parameters: mapper - It is a stateless function that will be applied to each element. Returns: A new transformed stream that contains the long elements
Write a program to get the length of words in a 2D array using the flatMapToLong method.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.Stream; public class FlatMapToLongExample { public static void main(String[] args) { String[][] words = { {"Hello", "we"}, {"are", "learning"}, {"flatMap", "method"} }; Stream<String[]> streamOfWords = Arrays.stream(words); LongStream longStream = streamOfWords.flatMapToLong(wordsArray -> Arrays.stream(wordsArray).mapToLong(word -> Long.valueOf(word.length()))); List<Long> lengthOfWords = longStream.boxed().collect(Collectors.toList()); // We are just getting the listOfWords for printing the words in the subsequent for loop List<String> listOfWords = Arrays.stream(words).flatMap(Arrays::stream).collect(Collectors.toList()); for (int i=0; i<listOfWords.size(); i++) { System.out.println("Length of " + listOfWords.get(i) + " is = " + lengthOfWords.get(i)); } } } Output: Length of Hello is = 5 Length of we is = 2 Length of are is = 3 Length of learning is = 8 Length of flatMap is = 7 Length of method is = 6
The Stream interface has a flatMapToDouble method that returns a DoubleStream using a mapping function. The DoubleStream is a sequence of primitive double-value elements that supports sequential and parallel aggregate operations.
DoubleStream flatMapToDouble(Function<? super T,? extends DoubleStream> mapper) Parameters: mapper - It is a stateless function that will be applied to each element. Returns: A new transformed stream that contains the double elements
Write a program to get the length of words in a 2D array and then multiply the length of each word by 2.5 using the flatMapToDouble method.
package com.javahandson.flatmap; import java.util.Arrays; import java.util.List; import java.util.stream.*; public class FlatMapToDoubleExample { public static void main(String[] args) { String[][] words = { {"Hello", "we"}, {"are", "learning"}, {"flatMap", "method"} }; Stream<String[]> streamOfWords = Arrays.stream(words); DoubleStream doubleStream = streamOfWords.flatMapToDouble(wordsArray -> Arrays.stream(wordsArray).mapToDouble(word -> Double.valueOf(word.length() * 2.5))); List<Double> lengthOfWords = doubleStream.boxed().collect(Collectors.toList()); // We are just getting the listOfWords for printing the words in the subsequent for loop List<String> listOfWords = Arrays.stream(words).flatMap(Arrays::stream).collect(Collectors.toList()); for (int i=0; i<listOfWords.size(); i++) { System.out.println("Length of " + listOfWords.get(i) + " is = " + lengthOfWords.get(i)); } } } Output: Length of Hello is = 12.5 Length of we is = 5.0 Length of are is = 7.5 Length of learning is = 20.0 Length of flatMap is = 17.5 Length of method is = 15.0
So this is all about flatMap in Java 8 streams with examples. If you have any questions on this topic please raise them in the comments section. If you liked this article then please share this post with your friends and colleagues so that they can learn about Java and its frameworks in more depth.