Java - Lambda Expressions & Streams API
Overview
Purpose
This tutorial introduces the new lambda expressions included in Java Platform Standard Edition 8 (Java
SE 8).
Time to Complete
Approximately 1 hour
Introduction
Lambda expressions are a new and important feature included in Java SE 8. They provide a clear and
concise way to represent one method interface using an expression. Lambda expressions also improve
the Collection libraries making it easier to iterate through, filter, and extract data from a Collection. In
addition, new concurrency features improve performance in multicore environments.
This Oracle by Example (OBE) provides an introduction to lambda expressions included in Java SE 8. An
introduction to anonymous inner functions is provided, followed by a discussion of functional interfaces
and the new lambda syntax. Then, examples of common usage patterns before and after lambda
expressions are shown.
The next section reviews a common search use case and how Java code can be improved with the
inclusion of lambda expressions. In addition, some of the common functional
interfaces, Predicate and Function, provided in java.util.function are shown in action.
The OBE finishes up with a review of how the Java collection has been updated with lambda expressions.
The source code for all examples is provided to you.
Hardware and Software Requirements
The following is a list of hardware and software requirements:
Java Development Kit (JDK 8) early access
NetBeans 7.4
Prerequisites
To run the examples, you must have an early access version of JDK 8 and a copy of NetBeans 7.4 or later.
Links can be found at the main Lambda site. For your convenience, here are the direct links.
Java Development Kit 8 (JDK8) Early Access
o Recommended: Download the JDK 8 API Docs from the same page.
NetBeans 7.4 or later. Version 7.4 of NetBeans includes support for JDK 8.
Note: Builds are provided for all major operating systems. This OBE was developed using Linux Mint 13
(Ubuntu/Debian).
Install JDK8 and NetBeans following the instructions provided with the downloads. Add
the bin directories for Java and NetBeans to your PATH.
Background
In Java, anonymous inner classes provide a way to implement classes that may occur only once in an
application. For example, in a standard Swing or JavaFX application a number of event handlers are
required for keyboard and mouse events. Rather than writing a separate event-handling class for each
event, you can write something like this.
Otherwise, a separate class that implements ActionListener is required for each event. By creating the
class in place, where it is needed, the code is a little easier to read. The code is not elegant, because
quite a bit of code is required just to define one method.
Functional Interfaces
The code that defines the ActionListener interface, looks something like this:
The ActionListener example is an interface with only one method. With Java SE 8, an interface that
follows this pattern is known as a "functional interface."
Note: This type of interface, was previously known as a Single Abstract Method type (SAM).
Using functional interfaces with anonymous inner classes are a common pattern in Java. In addition to
the EventListener classes, interfaces like Runnable and Comparator are used in a similar manner.
Therefore, functional interfaces are leveraged for use with lambda expressions.
Lambda Expression Syntax
Lambda expressions address the bulkiness of anonymous inner classes by converting five lines of code
into a single statement. This simple horizontal solution solves the "vertical problem" presented by inner
classes.
A lambda expression is composed of three parts.
The body can be either a single expression or a statement block. In the expression form, the body is
simply evaluated and returned. In the block form, the body is evaluated like a method body and a return
statement returns control to the caller of the anonymous method. The break and continue keywords are
illegal at the top level, but are permitted within loops. If the body produces a result, every control path
must return something or throw an exception.
Take a look at these examples:
The first expression takes two integer arguments, named x and y, and uses the expression form to
return x+y. The second expression takes no arguments and uses the expression form to return an
integer 42. The third expression takes a string and uses the block form to print the string to the console,
and returns nothing.
With the basics of syntax covered, let's look at some examples.
Lambda Examples
Here are some common use cases using the previously covered examples.
Runnable Lambda
Here is how you write a Runnable using lambdas.
In both cases, notice that no parameter is passed and is returned. The Runnable lambda expression,
which uses the block format, converts five lines of code into one statement.
Comparator Lambda
In Java, the Comparator class is used for sorting collections. In the following example,
an ArrayList consisting of Person objects is sorted based on surName. The following are the fields
included in the Person class.
The following code applies a Comparator by using an anonymous inner class and a couple lambda
expressions.
Lines 17 - 21 are easily replaced by the lambda expression on line 32. Notice that the first lambda
expression declares the parameter type passed to the expression. However, as you can see from the
second expression, this is optional. Lambda supports "target typing" which infers the object type from
the context in which it is used. Because we are assigning the result to a Comparator defined with a
generic, the compiler can infer that the two parameters are of the Person type.
Listener Lambda
Finally, let's revisit the ActionListener example.
Notice that the lambda expression is passed as a parameter. Target typing is used in a number of
contexts including the following:
Variable declarations
Assignments
Return statements
Array initializers
Method or constructor arguments
Lambda expression bodies
Conditional expressions ?:
Cast expressions
Improving Code with Lambda Expressions
This section builds upon the previous examples to show you how lambda expressions can improve your
code. Lambdas should provide a means to better support the Don't Repeat Yourself (DRY) principle and
make your code simpler and more readable.
A Common Query Use Case
A common use case for programs is to search through a collection of data to find items that match a
specific criteria. In the excellent "Jump-Starting Lambda" presentation at JavaOne 2012, Stuart Marks
and Mike Duigou walk though just such a use case. Given a list of people, various criteria are used to
make robo calls (automated phone calls) to matching persons. This tutorial follows that basic premise
with slight variations.
In this example, our message needs to get out to three different groups in the United States:
Drivers: Persons over the age of 16
Draftees: Male persons between the ages of 18 and 25
Pilots (specifically commercial pilots): Persons between the ages of 23 and 65
The actual robot that does all this work has not yet arrived at our place of business. Instead of calling,
mailing or emailing, a message is printed to the console. The message contains a person's name, age,
and information specific to the target medium (for example, email address when emailing or phone
number when calling).
Person Class
Each person in the test list is defined by using the Person class with the following properties:
The Person class uses a Builder to create new objects. A sample list of people is created with
the createShortList method. Here is a short code fragment of that method. Note: All source code for this
tutorial is included in a NetBeans project that is linked at the end of this section.
A First Attempt
With a Person class and search criteria defined, you can write a RoboContact class. One possible solution
defines a method for each use case:
RoboContactsMethods.java
Notice that a Predicate is set up for each group: allDrivers, allDraftees, and allPilots. You can pass any of
these Predicate interfaces to the contact methods. The code is compact and easy to read, and it is not
repetitive.
The java.util.function Package
The java.util.function Package
Of course, Predicate is not the only functional interface provided with Java SE 8. A number of standard
interfaces are designed as a starter set for developers.
Predicate: A property of the object passed as argument
Consumer: An action to be performed with the object passed as argument
Function: Transform a T to a U
Supplier: Provide an instance of a T (such as a factory)
UnaryOperator: A unary operator from T -> T
BinaryOperator: A binary operator from (T, T) -> T
In addition, many of these interfaces also have primitive versions. This should give you a great starting
point for your lambda expressions.
Eastern Style Names and Method References
When working on the previous example, I decided it would be nice to have a flexible printing system for
the Person class. One feature requirement is to display names in both a western style and an eastern
style. In the West, names are displayed with the given name first and the surname second. In many
eastern cultures, names are displayed with the surname first and the given name second.
An Old Style Example
Here is an example of how to implement a Person printing class without lambda support.
Person.java
A method exists for each style that prints out a person.
The Function Interface
The Function interface is useful for this problem. It has only one method apply with the following
signature:
public R apply(T t){ }
It takes a generic class T and returns a generic class R. For this example, pass the Person class and return
a String. A more flexible print method for person could be written like this:
Person.java
That is quite a bit simpler. A Function is passed to the method and a string returned. The apply method
processes a lambda expression which determines what Person information is returned.
How are the Functions defined? Here is the test code that calls the previous method.
NameTestNew.java
The first loop just prints given name and the email address. But any valid expression could be passed to
the printCustom method. Eastern and western print styles are defined with lambda expressions and
stored in a variable. The variables are then passed to the final two loops. The lambda expressions could
very easily be incorporated into a Map to make their reuse much easier. The lambda expression
provides a great deal of flexibility.
Sample Output
Here is some sample output from the program.
==== NameTestNew02 ===
===Custom List===
Name: Bob EMail:
[email protected]Name: Jane EMail:
[email protected]Name: John EMail:
[email protected]Name: James EMail:
[email protected]Name: Joe EMail:
[email protected]Name: Phil EMail: phil.smith@examp;e.com
Name: Betty EMail:
[email protected]===Western List===
Name: Bob Baker
Age: 21 Gender: MALE
EMail:
[email protected]Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Jane Doe
Age: 25 Gender: FEMALE
EMail:
[email protected]Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: John Doe
Age: 25 Gender: MALE
EMail:
[email protected]Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: James Johnson
Age: 45 Gender: MALE
EMail:
[email protected]Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111
Name: Joe Bailey
Age: 67 Gender: MALE
EMail:
[email protected]Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111
Name: Phil Smith
Age: 55 Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333
Name: Betty Jones
Age: 85 Gender: FEMALE
EMail:
[email protected]Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333
===Eastern List===
Name: Baker Bob
Age: 21 Gender: MALE
EMail:
[email protected]Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333
Name: Doe Jane
Age: 25 Gender: FEMALE
EMail:
[email protected]Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: Doe John
Age: 25 Gender: MALE
EMail:
[email protected]Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333
Name: Johnson James
Age: 45 Gender: MALE
EMail:
[email protected]Phone: 333-456-1233
Address: 201 2nd St, New York, NY 12111
Name: Bailey Joe
Age: 67 Gender: MALE
EMail:
[email protected]Phone: 112-111-1111
Address: 111 1st St, Town, CA 11111
Name: Smith Phil
Age: 55 Gender: MALE
EMail: phil.smith@examp;e.com
Phone: 222-33-1234
Address: 22 2nd St, New Park, CO 222333
Name: Jones Betty
Age: 85 Gender: FEMALE
EMail:
[email protected]Phone: 211-33-1234
Address: 22 4th St, New Park, CO 222333
Resources
The NetBeans project with the source code for the examples in this section is included in the following
zip file.
LambdaFunctionExamples.zip
Lambda Expressions and Collections
The previous section introduced the Function interface and finished up examples of basic lambda
expression syntax. This section reviews how lambda expressions improve the Collection classes.
Lambda Expressions and Collections
In the examples created so far, the collections classes were used quite a bit. However, a number of new
lambda expression features change the way collections are used. This section introduces a few of these
new features.
Class Additions
The drivers, pilots, and draftees search criteria have been encapsulated in the SearchCriteria class.
SearchCriteria.java
The Predicate based search criteria are stored in this class and available for our test methods.
Looping
The first feature to look at is the new forEach method available to any collection class. Here are a couple
of examples that print out a Person list.
Test01ForEach.java
The first example shows a standard lambda expression which calls the printWesternName method to
print out each person in the list. The second example demonstrates a method reference. In a case
where a method already exists to perform an operation on the class, this syntax can be used instead of
the normal lambda expression syntax. Finally, the last example shows how the printCustom method can
also be used in this situation. Notice the slight variation in variable names when one lambda expression
is included in another.
You can iterate through any collection this way. The basic structure is similar to the enhanced for loop.
However, including an iteration mechanism within the class provides a number of benefits.
Chaining and Filters
In addition to looping through the contents of a collection, you can chain methods together. The first
method to look at is filter which takes a Predicate interface as a parameter.
The following example loops though a List after first filtering the results.
Test02Filter.java
The first and last loops demonstrate how the List is filtered based on the search criteria. The output
from the last loop looks like following:
Getting Lazy
These features are useful, but why add them to the collections classes when there is already a perfectly
good for loop? By moving iteration features into a library, it allows the developers of Java to do more
code optimizations. To explain further, a couple of terms need definitions.
Laziness: In programming, laziness refers to processing only the objects that you want to
process when you need to process them. In the previous example, the last loop is "lazy" because
it loops only through the two Person objects left after the List is filtered. The code should be
more efficient because the final processing step occurs only on the selected objects.
Eagerness: Code that performs operations on every object in a list is considered "eager". For
example, an enhanced for loop that iterates through an entire list to process two objects, is
considered a more "eager" approach.
By making looping part of the collections library, code can be better optimized for "lazy" operations
when the opportunity arises. When a more eager approach makes sense (for example, computing a sum
or an average), eager operations are still applied. This approach is a much more efficient and flexible
than always using eager operations.
The stream Method
In the previous code example, notice that the stream method is called before filtering and looping begin.
This method takes a Collection as input and returns a java.util.stream.Stream interface as the output.
A Stream represents a sequence of elements on which various methods can be chained. By default, once
elements are consumed they are no longer available from the stream. Therefore, a chain of operations
can occur only once on a particular Stream. In addition, a Stream can be serial(default) or parallel
depending on the method called. An example of a parallel stream is included at the end of this section.
Mutation and Results
As previously mentioned, a Stream is disposed of after its use. Therefore, the elements in a collection
cannot be changed or mutated with a Stream. However, what if you want to keep elements returned
from your chained operations? You can save them to a new collection. The following code shows how to
do just that.
Test03toList.java
The collect method is called with one parameter, the Collectors class. The Collectors class is able to
return a List or Set based on the results of the stream. The example shows how the result of the stream
is assigned to a new List which is iterated over.
Calculating with map
The map method is commonly used with filter. The method takes a property from a class and does
something with it. The following example demonstrates this by performing calculations based on age.
Test04Map.java
And the output from the class is:
The program calculates the average age of pilots in our list. The first loop
demonstrates the old style of calculating the number by using a for loop. The second loop uses
the map method to get the age of each person in a serial stream. Notice that totalAge is a long.
The map method returns an IntSteam object, which contains a sum method that returns a long.
Note: To compute the average the second time, calculating the sum of ages is unnecessary. However, it
is instructive to show an example with the sum method.
The last loop computes the average age from the stream. Notice that the parallelStream method is used
to get a parallel stream so that the values can be computed concurrently. The return type is a bit
different here as well.
Resources
The NetBeans project with source code for the examples is in the following zip file.
LambdaCollectionExamples.zip
Summary
In this tutorial, you learned how to use:
Anonymous inner classes in Java.
Lambda expressions to replace anonymous inner classes in Java SE 8.
The correct syntax for lambda expressions.
A Predicate interface to perform searches on a list.
A Function interface to process an object and produce a different type of object.
New features added to Collections in Java SE 8 that support lambda expressions.
Resources
For further information on Java SE 8 and lambda expressions, see the following:
Java 8
Project Lambda
State of the Lambda
State of the Lambda Collections
Jump-Starting Lambda JavaOne 2012 (You Tube)
To learn more about Java and related topics check out the Oracle Learning Library.
Credits
Lead Curriculum Developer: Michael Williams
QA: Juan Quesada Nunez