The Difference Between map() and flatMap() Methods in Java
In this article, we are going to see the difference between map and flatMap methods in Java.
1. Introduction
Java has provided Stream interface since version 8. The map() and flatMap() are two intermediate operations. Here are the method signatures:
/* Returns a stream consisting of the results of applying the given function to the elements of this stream. Type Parameters: R - The element type of the new stream Parameters: mapper - a non-interfering, stateless function to apply to each element */ <R> Stream<R> map(Function<? super T, ? extends R> mapper) /* Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.) */ <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
Both map() and flatMap() accept a Function interface and return a Stream of objects. The difference is that map() transforms into an Object, but the flatMap() transforms into a Stream.

In this example, I will demonstrate:
- How to use
map()to transform aString, POJO, andListto another object in 1-to-1 mapping. - How to use
flatMap()to transform aString, POJO, andListto anotherStreamof objects.
2. Technologies Used
The example code in this article was built and run using:
- Java 11
- Maven 3.3.9
- Eclipse Oxygen
- Junit 4.12
3. Maven Project
3.1 Dependencies
I will include Junit in the pom.xml.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jcg.zheng.demo</groupId> <artifactId>java-map-flatmap-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
3.2 POJO
I will create a POJO which will be used to transform an object.
POJO.java
package jcg.zheng.demo;
public class POJO {
private int id;
private String name;
public POJO(int id, String name) {
super();
this.name = name;
this.id = id;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int nameWordCount() {
return name.length();
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
4. JUnit Test
4.1 Common Data
I will create a CommonData which includes several list of String, 2D array, and two lists of POJO. These constants will be used by all three test classes.
CommonData.java
package jcg.zheng.demo;
import java.util.Arrays;
import java.util.List;
public class CommonData {
protected static final List<String> stringList1 = Arrays.asList("A", "B", "C");
protected static final List<String> stringList2 = Arrays.asList("x", "y", "z");
protected static final List<String> lowerCaseStrings = Arrays.asList("mary", "something", "end");
protected static final List<List<String>> listOfStringLists = Arrays.asList(stringList2, stringList1);
protected static final String[][] string2DArray = new String[][] { { "apple", "pear" }, { "rice", "flour" },
{ "pork", "beef" } };
protected static final List<POJO> listOfObjects = Arrays.asList(new POJO(10, "Mary"), new POJO(20, "Zheng"),
new POJO(30, "Tom"), new POJO(40, "Johnson"));
protected static final List<POJO> listOfObjectWithNullNames = Arrays.asList(new POJO(10, null),
new POJO(20, "Zheng"), new POJO(30, "Tom"), new POJO(40, "Johnson"));
}4.2 MapTest
In this step, I will create a MapTest class which has six test methods.
testMaptoInt– converts aPOJOobject toLongwith thevalue of its name length.testMapWithListString - transforms two List of Strings.testMapWithPOJO - transforms a POJO to its name counttestMapWithPOJO_Exception - encounters a NullPointerException during the map operation.testMapWithPOJO_handleRuntimeException - makes sure the mapper does not throw any RuntimeException based onlogic.
MapTest.java
package jcg.zheng.demo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import jcg.zheng.demo.POJO;
public class MapTest extends CommonData {
private Integer countNameLength(POJO pojo) {
if (pojo != null && pojo.getName() != null) {
return pojo.getName().length();
}
return Integer.valueOf(0);
}
@Test
public void testMaptoInt() {
ToIntFunction<POJO> intMapper = POJO::nameWordCount;
int[] intArray = listOfObjects.stream().mapToInt(intMapper).toArray();
assertEquals(listOfObjects.size(), intArray.length);
}
@Test
public void testMapWithListString() {
List<Integer> listSizes = Stream.of(stringList2, stringList1).map(List::size).collect(Collectors.toList());
assertEquals(2, listSizes.size());
System.out.println(listSizes);
}
@Test
public void testMapWithPOJO() {
Function<POJO, Integer> countNameLength = POJO::nameWordCount;
List<Integer> nameCounts = listOfObjects.stream().map(countNameLength).collect(Collectors.toList());
assertEquals(nameCounts.size(), listOfObjects.size());
nameCounts.forEach(s -> System.out.println(s));
}
@Test(expected = NullPointerException.class)
public void testMapWithPOJO_Exception() {
Function<POJO, Integer> transform = POJO::nameWordCount;
listOfObjectWithNullNames.stream().map(transform).collect(Collectors.toList());
}
@Test
public void testMapWithPOJO_handleRuntimeException() {
Function<POJO, Integer> transform = this::countNameLength;
List<Integer> nameCounts = listOfObjectWithNullNames.stream().map(transform).collect(Collectors.toList());
assertEquals(nameCounts.size(), listOfObjectWithNullNames.size());
nameCounts.forEach(s -> System.out.println(s));
}
@Test
public void testMapWithString() {
Function<String, String> toUpper = String::toUpperCase;
List<String> allUppercase = lowerCaseStrings.stream().map(toUpper).collect(Collectors.toList());
assertEquals(lowerCaseStrings.size(), allUppercase.size());
assertTrue(allUppercase.contains("MARY"));
assertTrue(allUppercase.contains("SOMETHING"));
assertTrue(allUppercase.contains("END"));
}
}
Execute the Junit test and capture the output here:
Running jcg.zheng.demo.MapTest [3, 3] 4 5 3 7 0 5 3 7 Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.179 sec Results : Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
4.3 FlatMapTest
In this step, I will create a FlatMapTest class which has five test methods:
testFlatMapWith2DArray– converts a 2D array into aList<String>testFlatMapWithListofList– converts aListofList<String>intoList<String>. Flattening into a list of String.testFlatMapWithListStream– transformsListofList<String>to aList<String>testFlatMapWithUpperCaseString– transforms to uppercase String.
FlatMapTest.java
package jcg.zheng.demo;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
public class FlatMapTest extends CommonData {
private Stream<String> buildStreamOfUpperCaseString(List<String> item) {
String[] test = new String[item.size()];
for (int i = 0; i < test.length; i++) {
test[i] = item.get(i).toUpperCase();
}
return Stream.of(test);
}
@Test
public void testFlatMapWith2DArray() {
Stream<String> stringStream = Arrays.stream(string2DArray).flatMap(Arrays::stream);
assertEquals(3, string2DArray.length);
assertEquals(6, stringStream.collect(Collectors.toList()).size());
}
@Test
public void testFlatMapWithListofList() {
Function<List<String>, Stream<String>> toUpperFlatMapFunction = this::buildStreamOfUpperCaseString;
Stream.of(stringList2, stringList1, lowerCaseStrings).flatMap(toUpperFlatMapFunction)
.forEach(System.out::println);
}
@Test
public void testFlatMapWithListStream() {
Stream<String> stringStream = Stream.of(stringList2, stringList1).flatMap(List::stream);
assertEquals(3, stringList2.size());
assertEquals(3, stringList1.size());
assertEquals(6, stringStream.collect(Collectors.toList()).size());
}
@Test
public void testFlatMapWithLongStream() {
Function<List<String>, Stream<Long>> countFlatMapFunction = item -> Stream.of(item.stream().count());
Stream.of(stringList2, stringList1, lowerCaseStrings).flatMap(countFlatMapFunction)
.forEach(System.out::println);
}
@Test
public void testFlatMapWithUpperCaseString() {
Function<String, Stream<String>> toUppderFlatMapFunction = item -> Stream.of(item.toUpperCase());
stringList2.stream().flatMap(toUppderFlatMapFunction).forEach(System.out::println);
}
}
Execute it as unit test and capture output here.
Running jcg.zheng.demo.FlatMapTest 3 3 3 X Y Z X Y Z A B C MARY SOMETHING END Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.194 sec Results : Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
4.4 BothTest
Both map() and flatMap() return a Stream, so they can chain together too. In this step, I will create a BothTest class which has two test methods:
flatMap_Map– chainsmap()afterflatMap().map_flatMap– chainsflatMap()aftermap().
BothTest.java
package jcg.zheng.demo;
import static org.junit.Assert.assertEquals;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
public class BothTest extends CommonData {
@Test
public void flatMap_Map() {
List<String> flatedList = listOfStringLists.stream().flatMap(Collection::stream).map(String::toUpperCase)
.collect(Collectors.toList());
assertEquals(2, listOfStringLists.size());
assertEquals(6, flatedList.size());
}
@Test
public void map_flatMap() {
Function<String, Stream<String>> toUpper = item -> Stream.of(item.toUpperCase());
lowerCaseStrings.stream().map(String::toLowerCase).flatMap(toUpper).forEach(item -> {
System.out.println(item);
});
;
}
}Execute the Junit test and capture the output here.
Running jcg.zheng.demo.BothTest MARY SOMETHING END Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.285 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
5. Summary
In this example, I demonstrated how to use the map and flatMap methods. They are very similar functions. The difference between them is that the mapper function in flatMap() returns a Stream while the mapper function of map() return an object.
6. Download the Source Code
This example consists of a Maven project which contains several Junit tests to demonstrate the differences between Stream's map() and flatMap() methods.
You can download the full source code of this example here: The Difference Between map() and flatMap() Methods in Java

