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, andFunction
- 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 fourDemoPOJO
objectsvalidateTestObject
– validates the test objectsvalidateUpperCaseTransform
– 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 testsStream.map
with aFunction
for aString
object.string_lambda()
– it testsStream.map
with a lambda expression for aString
object.string_method_reference()
– it testsStream.map
with a method reference shorthand for aString
object.object_function()
– it testsStream.map
with aFunction
for aDemoPOJO
object.object_lambda()
– it testsStream.map
with a lambda expression for aDemoPOJO
object.object_method_reference()
– it testsStream.map
with a method reference shorthand for aDemoPOJO
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 chainsmap
to afilter
operation.filter_map()
– it chainsfilter
to amap
operation.map_map()
– it chainsmap
to anothermap
operation.map_peek()
– it chainsmap
to apeek
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 acollect
operation.map_filter_count()
– it chainsmap
to afilter
operation and returns the count.map_foreach()
– it terminates stream with aforeach
operationmap_toArray()
– it returns an object array with atoArray
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.
You can download the full source code of this example here: Java Stream Map Example
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.
Thanks. glad to hear it:)
I was looking for java stream map and I got exactly what I wanted. Thank you Mary Zheng
Thank you for post on Java Stream Map. It completes all details what I needed.