Java Streams map() Examples

In this article, we will look at Java Streams Map method. This method convert one object to another. Let’s look at some example to for a better understanding of Stream map() method.

Introduction

Stream map() is an intermediate operation used to apply one given function to the elements of a stream. It takes one function as its argument and applies it to each value of the stream and returns one fresh stream. In this tutorial, we will learn how to use the Java Streams map function with different examples.

 

1. Stream Map to Iterate and Change a List:

The traditional approach to iterate through a list is by using a for loop or any other loop. If we want any modification to the list, we can do it inside the loop block. Suppose we have one list of latitudes of positions in Double format and we want to make them more readable by appending ‘degrees’ to the end of each. For that, we will loop through the list of latitudes, convert each to String and append ‘degrees’ to the end.

Using a for loop, we can do it as like below:

List < Double > latitudes = Arrays.asList(18.520430, 18.530012, 18.54123, 18.552210, 18.565543, 18.577712);

for (Double lat: latitudes) {
 System.out.println(lat.toString() + " degrees");
}

Here, 'latitudes' is the list of latitudes. We are iterating through the list, converting each item to string and appending ‘degrees’ to the end.If we write the same process using the stream map(), it will look like below:

import java.util.Arrays;
import java.util.List;
class Main {
 public static void main(String[] args) {
  List < Double > latitudes = Arrays.asList(18.520430, 18.530012, 18.54123, 18.552210, 18.565543, 18.577712);

  latitudes.stream().map(lat -> lat.toString() + " degrees").forEach(System.out::println);
 }
}

First, we are converting the list to a stream. Then we are using one transform function to convert each stream item to our desired result and finally, we are using forEach to print out the formatted value. map() function takes one mapper function to apply on each stream element. It returns one new stream. forEach is used on this new stream. It takes one action that performs on each element of the stream. In our case, we are printing out the value of each item of the new stream.

Output:

18.52043 degrees
18.530012 degrees
18.54123 degrees
18.55221 degrees
18.565543 degrees
18.577712 degrees

 

2. Stream Map with a Parallel Stream:

We can use a stream map with a parallel stream. If we consider the above example, we can generate one parallel stream instead of a sequential stream. The stream() method generates one sequential stream. If we replace it with parallelStream(), it returns one parallel stream:

latitudes.parallelStream()
 .map(lat -> lat.toString() + " degrees")
 .forEach(System.out::println);

It will print a similar output, but the order will be different.

 

3. Stream Map example with Custom Objects:

Similar to the above example, we can use Java stream map() with custom objects. We want to store both latitude and longitude in a class object for specific users. We can create a class like below:

class UserLocation {
 private final String userName;
 private Double latitude;
 private Double longitude;

 UserLocation(String name, Double lat, Double lon) {
  this.latitude = lat;
  this.longitude = lon;
  this.userName = name;
 }

 public String getUserName() {
  return userName;
 }

 public Double getLatitude() {
  return latitude;
 }

 public void setLatitude(Double latitude) {
  this.latitude = latitude;
 }

 public Double getLongitude() {
  return longitude;
 }

 public void setLongitude(Double longitude) {
  this.longitude = longitude;
 }

 void printDetails() {
  System.out.println("Latitude: " + latitude + " Longitude: " + longitude);
}

UserName is a string to store the name of the user, latitude, and longitudes are Double values. These member variables are private and can be assigned using the constructor or using the setter methods. The printDetails method prints the details, i.e. the Latitude and Longitude of a UserLocation object.

We can have one list with latitude, longitudes of different users. The stream filter method can filter out data only for a specific user and map can format the data before printing out the values. For example:

class Main {
 private static UserLocation formatLocations(UserLocation location) {
  location.setLatitude(Double.parseDouble(String.format("%.3f", location.getLatitude())));
  location.setLongitude(Double.parseDouble(String.format("%.3f", location.getLongitude())));
  return location;
 }

 public static void main(String[] args) {
  UserLocation loc_1 = new UserLocation("user-1", 18.31142, 18.43345);
  UserLocation loc_2 = new UserLocation("user-2", 17.345603, 18.433456);
  UserLocation loc_3 = new UserLocation("user-1", 16.455453, 16.32212345);
  UserLocation loc_4 = new UserLocation("user-1", 16.3559894, 16.233456);
  UserLocation loc_5 = new UserLocation("user-2", 18.290876, 18.2234598);

  List < UserLocation > userLocations = Arrays.asList(loc_1, loc_2, loc_3, loc_4, loc_5);

  userLocations.stream().
  filter(loc -> loc.getUserName().equals("user-1"))
   .map(Main::formatLocations)
   .forEach(UserLocation::printDetails);
 }
}

The userLocations is a list of UserLocation objects. The filter method filtered out the objects only for "user-1". map method formatted the latitude and longitude values up to three decimal places. It uses the formatLocations method for the conversion. Finally, we are using forEach to print out the details of each.

 

4. Example of Stream Filter, Map and Collect:

In the above example, we have used mapper function to format all latitude and longitude values up to three decimal places. You can do any change to an object using a mapper function. If you look closely; we have one unnecessary field in the objects of the above example.

If we want to store the filtered list in a different variable, we can remove the property userName because all these objects will be for a specific user. The name will be the same for all. In such scenarios, we can create one distinct class to hold only the required data and using map(), we can convert one type to another.

I will continue with the above example. Create one new class Location to store only latitude and longitude :

class Location {
 private final Double latitude;
 private final Double longitude;

 Location(Double lat, Double lon) {
  this.latitude = lat;
  this.longitude = lon;
 }

 public Double getLongitude() {
  return longitude;
 }

 public Double getLatitude() {
  return latitude;
 }
}

Both latitude and longitude are private final variables and assigned only using the constructor. Also, we need to change the formatLocations method as like below:

private static Location formatLocations(UserLocation location) {
 return new Location(location.getLatitude(), location.getLongitude());
}

This method takes one UserLocation object and converts it to a Location object.Now, we can use map() to do the mapping:

List < Location > locations = userLocations.stream()
 .filter(loc -> loc.getUserName().equa("user-1"))
 .map(Main::formatLocations)
 .collect(Collectors.toList());

It will filter out a list of UserLocation for a specific user, formats each UserLocation to Location, and converts it to a list of Location. If we have big objects with unnecessary parameters, we can convert it to a new class object with only the required parameters.

 

5. Example to Find One Value in a Stream using Map():

For this example, I will use one HashMap. We will store user data in this HashMap. HashMap is used to store key-value pairs. The key is user phone number and value is the username.

Map < String, String > userMap = new HashMap<>();
userMap.put("11001234", "Alex");
userMap.put("11003333", "James");
userMap.put("11223432", "William");
userMap.put("11989876", "Noah");
userMap.put("11897867", "Olivia");

The userMap is the HashMap we are using to store the username and phone numbers. Both phone number and name are of string type. We can’t have two items with the same key or phone number. We have added five users with different phone numbers. Now, if we want to get the username for a specific phone number, we can do it as like below:

Optional < String > userName = userMap.entrySet()
 .stream()
 .filter(user -> user.getKey().equals("11989876"))
 .map(Map.Entry::getValue)
 .findFirst();

System.out.println(userName.get());

Here, by using entrySet(), we got one set view of the map. We used stream() and filter to get one sequential stream and filter the item with a key equal to the user phone number. The map method returns the value for that key.

This example returns the first value in the stream that satisfies the conditions. We have a HashMap, so we are sure that we can’t have two items with the same key. If we use any other type, it might return an unexpected result. Use findFirst only for a unique set of items. We can also use collect(Collectors.toList()) to collect the data to a list if we have multiple values that satisfy the condition.

 

6. Stream Map with Optional:

We use Optional class in java as a container class. It may or may not hold a non-null value. It provides one method isPresent() that returns one boolean value showing if a value is present or not in that object. If it is non-null, get() can get that value. We can use stream map() with a list of optionals to filter out all non-null values. For example, let’s consider the below list of optional strings:

List < Optional > strList = Arrays.asList(Optional.of("Sun"), Optional.empty(),
 Optional.of("Mon"),
 Optional.of("Tues"),
 Optional.empty(),
 Optional.of("Wed"),
 Optional.empty(),
 Optional.of("Thurs"),
 Optional.empty(),
 Optional.of("Fri"),
 Optional.of("Sat"),
 Optional.empty());

To filter out all non-null values from this list, we can use filter, map, and collect as like below :

List < String > filteredList = strList.stream()
 .filter(Optional::isPresent)
 .map(Optional::get)
 .collect(Collectors.toList());

It returns one list of string values. The filter method filters all non-null optionals, map() method gets the value, and collect method collects it in a list.

 

7. Difference Between map() vs flatMap():

flatMap is used to flatten a list. It is like map(), i.e. it takes one mapper function. But it is mainly used to flatten a Stream of collections to a stream of elements. For example, if we have a list of lists:

[
 ["Sun", "Mon"],
 ["Tues", "wed", "Thurs"],
 ["Fri", "Sat"]
]

We can use flatMap to flatten it something like below :

["Sun", "Mon", "Tues", "wed", "Thurs", "Fri", "Sat"]

If I write it in code, it looks as like below :

List  daysOne = Arrays.asList("Sun", "Mon");
List  daysTwo = Arrays.asList("Tues", "wed", "Thurs");
List  daysThree = Arrays.asList("Fri", "Sat");

List <List> days = Arrays.asList(daysOne,
 daysTwo,
 daysThree);

List  daysStrList = days.stream()
 .flatMap(Collection::stream)
 .collect(Collectors.toList());
System.out.println(daysStrList);

days is a list of “list of strings” and we used flatMap to flatten it to daysStrList which is a list of strings. It will print the below output :

[Sun, Mon, Tues, wed, Thurs, Fri, Sat]

We can also do any transformation during flattering.This is the main difference between map() and flatMap() i.e. map() is used only to transform one stream but flatMap() is used to transform as well as flattering a stream.

For more details, read The Difference Between map() and flatMap()

 

Summary:

In this post, we looked at Java Streams Map method. We have seen different examples of the map() method of Stream API. Steam map() makes it easy to apply one mapping function to all elements of a stream. flatMap( ) is also useful if we want to flatten a stream. Using these methods with filter() and collect() can reduce a lot of effort on different types of stream operations.