Java 8 – Difference between map() and flatMap() in Stream API

In this article, we will discuss difference between map() and flatMap() methods of Stream API introduced in Java 1.8 version

Both these map methods of Stream API of course does transformation but their usage is different for different types of input values

Before understanding differences between them, we will quickly go through brief explanation with examples for both methods

1. map()

  • This Stream method is an intermediate operation which is stateless and non-interfering with other elements in the Stream
  • This method used to transform one set of values into another set of values by applying given function
  • Transformation :- That’s when map function is applied to Stream of T type (Stream<T>) then it get converted to Stream of R type (Stream<R>)
  • One-to-One mapping :- map() method produces single value for each of the elements in the input stream hence it is referred as One-to-One mapping
  • Example 1 :- a map function to square the input values can be applied to Stream of Integer consisting of natural numbers, then new Stream of Integer is returned consisting of its square values
  • Example 2 :- another example is finding ranks of Student from the input List of Students
  • Note :- Number of elements returned in the new Stream after applying map function will always be equal to number of elements in the Original Stream
  • Method signature :- <R> Stream<R> map(Function<? super T, ? extends R> mapper)

1.1 Example to convert Stream of Integers into Stream of Square values

  • Here, we have list of first few natural numbers
  • Using map function, we are going to transform into new stream of its square values
package net.bench.resources.stream.flatmap.example;

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

public class StreamMapTransformSquare {

	public static void main(String[] args) {

		// List of first few natural numbers
		List<Integer> listOfNaturalNumbers = Arrays.asList(1, 2, 3, 4, 5);

		System.out.println("1. Original Natural numbers : \n\n" + listOfNaturalNumbers);


		// use map function to convert to Square value
		List<Integer> squareValues = listOfNaturalNumbers
				.stream() // 1. get stream
				.map(n -> n*n) // 2. map intermediate operation
				.collect(Collectors.toList()); // 3. collect terminal operation


		System.out.println("\n\n2. Sqauer values of above Natural numbers : \n");

		// print to console using Java 8 forEach
		squareValues.forEach(i -> System.out.println(i));
	}
}

Output:

1. Original Natural numbers : 

[1, 2, 3, 4, 5]


2. Sqauer values of above Natural numbers : 

1
4
9
16
25

1.2 Example to find ranks of each Students from List of Student object

  • Here, we have Student object with attributes like name, marks, ranks, etc.
  • Using map function, we are going to pass Student object and get rank for each Student
  • Note: we can also apply another intermediate operation to sort ranks obtained using sorted(String::compareTo) method of Stream API
package net.bench.resources.stream.flatmap.example;

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

class Student {

	// member variables

	String name;
	int marks;
	int rank;

	// public parameterized constructor

	public Student(String name, int marks, int rank) {
		super();
		this.name = name;
		this.marks = marks;
		this.rank = rank;
	}

	// override toString() method

	@Override
	public String toString() {
		return "Student [name=" + name + ", marks=" + marks + ", rank=" + rank + "]";
	}
}

public class StreamMapStudentRanks {

	public static void main(String[] args) {

		// List of Students
		List<Student> studentList = Arrays.asList(
				new Student("Vijay", 97, 1),
				new Student("Ajith", 71, 3),
				new Student("Surya", 64, 4),
				new Student("Arya", 83, 2),
				new Student("Siva", 55, 5)
				);

		System.out.println("1. Student list with all attributes :- \n");

		// print to console using Java 8 forEach
		studentList.forEach(System.out::println);



		// getting ranks of each Student from List
		List<Integer> rankList = studentList
				.stream() // 1. get stream
				.map(student -> student.rank) // 2. map intermediate operation
				.collect(Collectors.toList()); // 3. collect terminal operation

		System.out.println("\n\n2. Ranks of all Students from List :- \n");

		// print to console using Java 8 forEach
		rankList.forEach(rank -> System.out.println(rank));
	}
}

Output:

1. Student list with all attributes :- 

Student [name=Vijay, marks=97, rank=1]
Student [name=Ajith, marks=71, rank=3]
Student [name=Surya, marks=64, rank=4]
Student [name=Arya, marks=83, rank=2]
Student [name=Siva, marks=55, rank=5]


2. Ranks of all Students from List :- 

1
3
4
2
5

2. flatMap()

  • This Stream method is an intermediate operation which is stateless and non-interfering with other elements in the Stream
  • map method does only transformation; but flatMap does mapping as well as flattening and this is main difference between these 2 map method of Stream API
  • Suppose, we have List of List of String elements, in this case direct transformation isn’t possible. So, we have to map first and then flatten to get List of String elements
  • Transformation and Flattening :- When flatMap function is applied to Stream of Stream of T type (Stream<Stream<T>>) then it get converted to Stream of R type (Stream<R>) i.e.; transform to another stream and next flatten it
  • One-to-Many mapping :- flatMap() method produces stream of values for each of the elements in the input stream hence it is referred as One-to-Many mapping
  • Note :- Number of elements returned in the new Stream after transformation and flattening will always be equal to sum of elements in all sub-Streams
  • Method signature :- <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

2.1 flattening

  • Flattening is basically converting all sub-lists into single list
  • That’s Collection<Collection<T>> to Collection<T>
  • For example, 3 list containing String elements and these 3 lists is added to the outer list, then applying flatMap produces one single list consisting of all String elements present in 3 sub-lists
List of List of String elements - before flattening :-

[
 [Apple, Banana, WaterMelon], 
 [MuskMelon, Pomegranate, Papaya], 
 [Pineapple, Chikko, Orange, Grapes]
]


After flattening :-

[Apple, Banana, WaterMelon, MuskMelon, Pomegranate, Papaya, Pineapple, Chikko, Orange, Grapes]

2.2 Example to convert 3 sub-list of String elements into one list

  • Initially, there are 3 list containing String elements
  • we added these 3 lists into outer list
  • Then applied flapMap method to transform and flatten to store inside single list consisting all String elements from each of 3 sub lists
package net.bench.resources.stream.flatmap.example;

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

public class StreamFlatMapTransformation {

	public static void main(String[] args) {

		// 1. create 1st List with 3 String elements
		List<String> firstList = Arrays.asList("Apple", "Banana", "WaterMelon");

		// 2. create 1st List with 3 String elements
		List<String> secondList = Arrays.asList("MuskMelon", "Pomegranate", "Papaya");

		// 3. create 1st List with 3 String elements
		List<String> thirdList = Arrays.asList("Pineapple", "Chikko", "Orange", "Grapes");

		// finally, create List of Lists
		List<List<String>> fruitsList = Arrays.asList(
				firstList, 
				secondList, 
				thirdList
				);

		System.out.println("1. Before flatMap and flattening :- \n\n"
				+ fruitsList);


		// merge List of List of String into single List
		List<String> resultingList = fruitsList
				.stream() // 1. get stream
				.flatMap(list -> list.stream()) // 2. intermediate operation
				.collect(Collectors.toList()); // 3. terminal operation

		System.out.println("\n\n2. Merging List of Lists into single List :- \n\n"
				+ resultingList);
	}
}

Output:

1. Before flatMap and flattening :- 

[[Apple, Banana, WaterMelon], [MuskMelon, Pomegranate, Papaya], 
[Pineapple, Chikko, Orange, Grapes]]


2. Merging List of Lists into single List :- 

[Apple, Banana, WaterMelon, MuskMelon, Pomegranate, Papaya, Pineapple, Chikko, Orange, Grapes]

3. Difference between map v/s flatMap :

  • map() method does only transformation/mapping; whereas flatMap() method does mapping as well as flattening
  • map() method produces single output for each elements in input Stream; whereas flatMap() produces Stream of values or List of values for each input value
  • map() method is referred as One-to-One mapping as there is one output for every input; whereas flatMap() method is referred as One-to-Many mapping as for each input it produces Stream of values
  • Transformation for map() method is from Stream<T> to Stream<R>
  • Transformation and flattening for flatMap() method is from Stream<Stream<T>> to Stream<R>

References:

Happy Coding !!
Happy Learning !!

Java 8 - Stream forEach method with examples
Java 8 - Stream flapMap method with examples