Java - Lambda Expressions & Streams API
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.
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.
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 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.
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.
Lambda Examples
Here are some common use cases using the previously covered examples.
Runnable Lambda
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
Variable declarations
Assignments
Return statements
Array initializers
Conditional expressions ?:
Cast 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 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
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.
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.
Function: Transform a T to a U
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.
Here is an example of how to implement a Person printing class without lambda support.
Person.java
The Function interface is useful for this problem. It has only one method apply with the following
signature:
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
===Western List===
Resources
The NetBeans project with the source code for the examples in this section is included in the following
zip file.
LambdaFunctionExamples.zip
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.
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.
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.
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.
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
Resources
For further information on Java SE 8 and lambda expressions, see the following:
Java 8
Project Lambda
To learn more about Java and related topics check out the Oracle Learning Library.
Credits