Core Java

Java Stream Map Example

1. Introduction

Java has provided Stream API to iterate and process elements in a collection with a declarative style since version 8. A stream is a sequence of objects that can be constructed via the default java.util.Collection.stream() method, static java.util.Arrays.stream() method, static java.nio.file.Files.lines() method, etc. Streams don’t change the element data. Stream API provides intermediate and terminal operations to process elements and return a result.

We can use its map method to transform elements in a collection of objects without using a loop statement. The map method is an intermediate operation which transforms a stream into another stream via a function. Here is the syntax:

/*
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)

In this example, I will demonstrate:

  • Three ways to use the map method: lambda expression, method reference, and Function
  • Chain the map method with intermediate operations: filter, peek, etc
  • Close the map method with terminal operations: foreach, count, collect, toArray, etc

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-stream-map-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 Demo POJO

I will create a DemoPOJO which will be used to transform data.

DemoPOJO.java

package jcg.zheng.demo;

public class DemoPOJO {

	private int id;

	private String name;

	public DemoPOJO(int id, String name) {
		super();
		this.name = name;
		this.id = id;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		DemoPOJO other = (DemoPOJO) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	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 TestBase

In this step, I will create a TestBase class which has two lists and two methods.

  • lowerCaseStrings – a list with three strings: "mary", "something", "end"
  • testObjects – a list with four DemoPOJO objects
  • validateTestObject – validates the test objects
  • validateUpperCaseTransform – validates the test strings

TestBase.java

package jcg.zheng.demo.stream;

import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.List;

import jcg.zheng.demo.DemoPOJO;

public class TestBase {

	protected List<String> lowerCaseStrings = Arrays.asList("mary", "something", "end");

	protected List<DemoPOJO> testObjects = Arrays.asList(new DemoPOJO(10, "Mary"), new DemoPOJO(20, "Zheng"),
			new DemoPOJO(30, "Tom"), new DemoPOJO(40, "Johnson"));

	protected void validateTestObject(List<String> names) {
		assertTrue(names.contains("Mary"));
		assertTrue(names.contains("Zheng"));
		assertTrue(names.contains("Tom"));
		assertTrue(names.contains("Johnson"));
	}

	protected void validateUpperCaseTransform(List<String> allUppercase) {
		assertTrue(allUppercase.contains("MARY"));
		assertTrue(allUppercase.contains("SOMETHING"));
		assertTrue(allUppercase.contains("END"));
	}

}

4.2 StreamMapTest

In this step, I will create a StreamMapTest class which shows three ways to create a function used in a map method.

  • string_function() – it tests Stream.map with a Function for a String object.
  • string_lambda() – it tests Stream.map with a lambda expression for a String object.
  • string_method_reference() – it tests Stream.map with a method reference shorthand for a String object.
  • object_function() – it tests Stream.map with a Function for a DemoPOJO object.
  • object_lambda() – it tests Stream.map with a lambda expression for a DemoPOJO object.
  • object_method_reference() – it tests Stream.map with a method reference shorthand for a DemoPOJO object.

StreamMapTest.java

package jcg.zheng.demo.stream;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.junit.Test;

import jcg.zheng.demo.DemoPOJO;

public class StreamMapTest extends TestBase {

	@Test
	public void string_function() {
		Function<String, String> toUpper = String::toUpperCase;
		List<String> allUppercase = lowerCaseStrings.stream().map(toUpper).collect(Collectors.toList());
		validateUpperCaseTransform(allUppercase);
	}

	@Test
	public void string_lambda() {
		List<String> allUppercase = lowerCaseStrings.stream().map(n -> n.toUpperCase()).collect(Collectors.toList());
		validateUpperCaseTransform(allUppercase);
	}

	@Test
	public void string_method_reference() {
		List<String> allUppercase = lowerCaseStrings.stream().map(String::toUpperCase).collect(Collectors.toList());
		validateUpperCaseTransform(allUppercase);
	}

	@Test
	public void object_lambda() {
		List<String> names = testObjects.stream().map(obj -> obj.getName()).collect(Collectors.toList());
		validateTestObject(names);
	}

	@Test
	public void object_function() {
		Function<DemoPOJO, Integer> transform = DemoPOJO::nameWordCount;
		List<Integer> nameCounts = testObjects.stream().map(transform).collect(Collectors.toList());
		nameCounts.forEach(s -> System.out.println(s));
	}

	@Test
	public void object_methodReference() {
		List<String> names = testObjects.stream().map(DemoPOJO::getName).collect(Collectors.toList());
		validateTestObject(names);
	}

}

Output

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running jcg.zheng.demo.stream.StreamMapTest
4
5
3
7
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.144 sec

Results :

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

4.3 StreamMap_IntermediateOperationTest

In this step, I will create a StreamMap_IntermediateOperationTest class which chains the map method to Stream‘s intermediate operations before and after.

  • map_filter() – it chains map to a filter operation.
  • filter_map() – it chains filter to a map operation.
  • map_map() – it chains map to another map operation.
  • map_peek() – it chains map to a peek operation.

StreamMap_IntermediateOperationTest.java

package jcg.zheng.demo.stream;

import static org.junit.Assert.assertEquals;

import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.junit.Test;

public class StreamMap_IntermediateOperationTest extends TestBase {
	
	@Test
	public void filter_map() {
		Predicate<String> shortName = name -> name.length() < 4;
		List<String> shortNames = lowerCaseStrings.stream().filter(shortName).map(String::toUpperCase)
				.collect(Collectors.toList());
		assertEquals("END", shortNames.get(0));
	}

	@Test
	public void map_filter() {
		Predicate<String> shortName = name -> name.length() < 4;
		List<String> shortNames = lowerCaseStrings.stream().map(String::toUpperCase).filter(shortName)
				.collect(Collectors.toList());
		assertEquals("END", shortNames.get(0));
	}

	@Test
	public void map_map() {
		Function<String, String> prefix = n -> "PRE_" + n;
		lowerCaseStrings.stream().map(prefix).map(String::toUpperCase).forEach(e -> System.out.println(e));
	}
	
	@Test
	public void map_peek() {
		//peek is for debugging purpose to see the elements in the Stream
		lowerCaseStrings.stream().map(String::toUpperCase).peek(e -> System.out.println(e)).collect(Collectors.toList());
	}

}

Output of mvn test -Dtest=StreamMap_IntermediateOperationTest

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running jcg.zheng.demo.stream.StreamMap_IntermediateOperationTest
MARY
SOMETHING
END
PRE_MARY
PRE_SOMETHING
PRE_END
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.209 sec

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

4.4 StreamMap_TerminalOperationTest

In this step, I will create a StreamMap_TerminalOperationTest class which chains the map method to a Stream’s terminal operation which produces a result or side-effect.

  • map_collect() – it returns a list with a collect operation.
  • map_filter_count() – it chains map to a filter operation and returns the count.
  • map_foreach() – it terminates stream with a foreach operation
  • map_toArray() – it returns an object array with a toArray operation

StreamMap_TerminalOperationTest.java

package jcg.zheng.demo.stream;

import static org.junit.Assert.assertEquals;

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

import org.junit.Test;

public class StreamMap_TerminalOperationTest extends TestBase {

	@Test
	public void map_collect() {
		List<String> allUppercase = lowerCaseStrings.stream().map(String::toUpperCase).collect(Collectors.toList());
		validateUpperCaseTransform(allUppercase);
	}

	@Test
	public void map_filter_count() {
		long maryCount = lowerCaseStrings.stream().map(String::toUpperCase).filter(e -> e.equalsIgnoreCase("MARY"))
				.count();
		assertEquals(1, maryCount);
	}

	@Test
	public void map_foreach() {
		lowerCaseStrings.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));
	}

	@Test
	public void map_toArray() {
		Object[] upperArr = lowerCaseStrings.stream().map(String::toUpperCase).toArray();
		assertEquals("MARY", upperArr[0]);
		assertEquals("SOMETHING", upperArr[1]);
		assertEquals("END", upperArr[2]);
	}
}

Output of mvn test -Dtest=StreamMap_TerminalOperationTest

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running jcg.zheng.demo.stream.StreamMap_TerminalOperationTest
MARY
SOMETHING
END
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.176 sec

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

5. Java Stream Map – Summary

In this example, I demonstrated how to use the Stream.map method via a lambda expression, function, and method reference shorthand. I also demonstrated how to chain the map method to intermediate operations and a terminal operation.

As you seen in these example, Stream.map provides a declarative way to transform the collection element without explicitly using a loop.

6. Download the Source Code

This example consists of a Maven project which contains several Junit tests to demonstrate the usage of Stream.map method.

Download
You can download the full source code of this example here: Java Stream Map Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mark Hooker
4 years ago

Hey Mary Zheng, thank you for such an informative post on Java Stream Map. It’s been really helpful for me to understand the concept in detail.

FP Markets
3 years ago

I was looking for java stream map and I got exactly what I wanted. Thank you Mary Zheng

Lance Burstyn
3 years ago

Thank you for post on Java Stream Map. It completes all details what I needed.

Back to top button