Using Streams and Lambda Expressions in Java

By Barry A. Burd

Java has fancy methods that make optimal use of streams and lambda expressions. With streams and lambda expressions, you can create an assembly line. The assembly-line solution uses concepts from functional programming.

The assembly line consists of several methods. Each method takes the data, transforms the data in some way or other, and hands its results to the next method in line. Here’s an assembly line.

assembly line in java
A functional programming assembly line.

Each box represents a bunch of raw materials as they’re transformed along an assembly line. Each arrow represents a method (or, metaphorically, a worker on the assembly line).

For example, in the transition from the second box to the third box, a worker method (the filter method) sifts out sales of items that aren’t DVDs. Imagine Lucy Ricardo standing between the second and third boxes, removing each book or CD from the assembly line and tossing it carelessly onto the floor.

The parameter to Java’s filter method is a Predicate — a lambda expression whose result is boolean. The filter method sifts out items that don’t pass the lambda expression’s true / false test.

Some Functional Programming Methods
Method Name Member Of Parameter(s) Result Type Result Value
stream Collection (for example, an ArrayList, object) (none) Stream A stream that spits out elements of the collection
filter Stream Predicate Stream A new stream containing values for which the lambda expression returns true
map Stream Function Stream A new stream containing the results of applying the lambda expression to the incoming stream
reduce Stream BinaryOperator The type used by the BinaryOperator The result of combining all the values in the incoming stream

In the transition from the third box to the fourth box, a worker method (the map method) pulls the price out of each sale. From that worker’s place onward, the assembly line contains only price values.

To be more precise, Java’s map method takes a Function such as

(sale) -> sale.getPrice()

and applies the Function to each value in a stream. So the map method takes an incoming stream of sale objects and creates an outgoing stream of price values.

In the transition from the fourth box to the fifth box, a worker method (the reduce method) adds up the prices of DVD sales. Java’s reduce method takes two parameters:

The first parameter is an initial value.

In the image above, the initial value is 0.0.

The second parameter is a BinaryOperator.

In the image above, the reduce method’s BinaryOperator is

(price1, price2) -> price1 + price2

The reduce method uses its BinaryOperator to combine the values from the incoming stream. The initial value serves as the starting point for all the combining. So, the reduce method does two additions.

java-7e-reduce-method
The reduce method adds two values from an incoming stream.

For comparison, imagine calling the method

reduce(10.0, (value1, value2) -> value1 * value2)

with the stream whose values include 3.0, 2.0, and 5.0. The resulting action is shown below

multiplied reduce method in java
The reduce method multiplies values from an incoming stream.

You might have heard of Google’s MapReduce programming model. The similarity between the programming model’s name and the Java method names map and reduce is not a coincidence.

Taken as a whole, the entire assembly line up the prices of DVDs sold. The code above contains a complete program using the streams and lambda expressions the first image above.

import java.text.NumberFormat;

import java.util.ArrayList;

 

public class TallySales {

 

public static void main(String[] args) {

ArrayList<Sale> sales = new ArrayList<>();

NumberFormat currency = NumberFormat.getCurrencyInstance();

 

fillTheList(sales);

double total = sales.stream()

.filter((sale) -> sale.getItem().equals("DVD"))

.map((sale) -> sale.getPrice())

.reduce(0.0, (price1, price2) -> price1 + price2);

 

System.out.println(currency.format(total));

}

 

static void fillTheList(ArrayList<Sale> sales) {

sales.add(new Sale("DVD", 15.00));

sales.add(new Sale("Book", 12.00));

sales.add(new Sale("DVD", 21.00));

sales.add(new Sale("CD", 5.25));

}

}

The code requires Java 8 or later. If your IDE is set for an earlier Java version, you might have to tinker with the IDE’s settings. You may even have to download a newer version of Java.

The boldface is one big Java assignment statement. The right side of the statement contains a sequence of method calls. Each method call returns an object, and each such object is the thing before the dot in the next method call. That’s how you form the assembly line.

For example, near the start of the boldface code, the name sales refers to an ArrayList object. Each ArrayList object has a stream method. In the code above, sales.stream() is a call to that ArrayList object’s stream method.

The stream method returns an instance of Java’s Stream class. (What a surprise!) So sales.stream() refers to a Stream object.

stream object in java
Getting all DVD sales.

Every Stream object has a filter method. So

sales.stream().filter⁣((sale) -> sale.getItem().equals("DVD"))

is a call to the ⁣Stream object’s filter method.

The pattern continues. The Stream object’s map method returns yet another Stream object — a Stream object containing prices.

stream object with prices in java
Getting the price from each DVD sale.

To that Stream of prices you apply the reduce method, which yields one double value — the total of the DVD prices.

stream object total price java
Getting the total price of all DVD sales.