Java SE 8: Lambda Quick Start
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
- 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.
Note: This OBE was last updated December 2013.
Background
Anonymous Inner Class
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.
16 JButton testButton = new JButton("Test Button");
17 testButton.addActionListener(new ActionListener(){
18 @Override public void actionPerformed(ActionEvent ae){
19 System.out.println("Click Detected by Anon Class");
20 }
21 });
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:
1 package java.awt.event;
2 import java.util.EventListener;
3
4 public interface ActionListener extends EventListener {
5
6 public void actionPerformed(ActionEvent e);
7
8 }
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.
Argument List | Arrow Token | Body |
(int x, int y) |
-> |
x + y |
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:
(int x, int y) -> x + y () -> 42 (String s) -> { System.out.println(s); }
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.
6 public class RunnableTest {
7 public static void main(String[] args) {
8
9 System.out.println("=== RunnableTest ===");
10
11 // Anonymous Runnable
12 Runnable r1 = new Runnable(){
13
14 @Override
15 public void run(){
16 System.out.println("Hello world one!");
17 }
18 };
19
20 // Lambda Runnable
21 Runnable r2 = () -> System.out.println("Hello world two!");
22
23 // Run em!
24 r1.run();
25 r2.run();
26
27 }
28 }
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.
9 public class Person {
10 private String givenName;
11 private String surName;
12 private int age;
13 private Gender gender;
14 private String eMail;
15 private String phone;
16 private String address;
17
The following code applies a Comparator
by using an
anonymous inner class and a couple lambda expressions.
10 public class ComparatorTest {
11
12 public static void main(String[] args) {
13
14 List<Person> personList = Person.createShortList();
15
16 // Sort with Inner Class
17 Collections.sort(personList, new Comparator<Person>(){
18 public int compare(Person p1, Person p2){
19 return p1.getSurName().compareTo(p2.getSurName());
20 }
21 });
22
23 System.out.println("=== Sorted Asc SurName ===");
24 for(Person p:personList){
25 p.printName();
26 }
27
28 // Use Lambda instead
29
30 // Print Asc
31 System.out.println("=== Sorted Asc SurName ===");
32 Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
33
34 for(Person p:personList){
35 p.printName();
36 }
37
38 // Print Desc
39 System.out.println("=== Sorted Desc SurName ===");
40 Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
41
42 for(Person p:personList){
43 p.printName();
44 }
45
46 }
47 }
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.
13 public class ListenerTest {
14 public static void main(String[] args) {
15
16 JButton testButton = new JButton("Test Button");
17 testButton.addActionListener(new ActionListener(){
18 @Override public void actionPerformed(ActionEvent ae){
19 System.out.println("Click Detected by Anon Class");
20 }
21 });
22
23 testButton.addActionListener(e -> System.out.println("Click Detected by Lambda Listner"));
24
25 // Swing stuff
26 JFrame frame = new JFrame("Listener Test");
27 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
28 frame.add(testButton, BorderLayout.CENTER);
29 frame.pack();
30 frame.setVisible(true);
31
32 }
33 }
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
Resources
A NetBeans project containing the source files for the examples covered to this point is in the following zip file.
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:
10 public class Person {
11 private String givenName;
12 private String surName;
13 private int age;
14 private Gender gender;
15 private String eMail;
16 private String phone;
17 private String address;
18
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.
128 public static List<Person> createShortList(){
129 List<Person> people = new ArrayList<>();
130
131 people.add(
132 new Person.Builder()
133 .givenName("Bob")
134 .surName("Baker")
135 .age(21)
136 .gender(Gender.MALE)
137 .email("bob.baker@example.com")
138 .phoneNumber("201-121-4678")
139 .address("44 4th St, Smallville, KS 12333")
140 .build()
141 );
142
143 people.add(
144 new Person.Builder()
145 .givenName("Jane")
146 .surName("Doe")
147 .age(25)
148 .gender(Gender.FEMALE)
149 .email("jane.doe@example.com")
150 .phoneNumber("202-123-4678")
151 .address("33 3rd St, Smallville, KS 12333")
152 .build()
153 );
154
155 people.add(
156 new Person.Builder()
157 .givenName("John")
158 .surName("Doe")
159 .age(25)
160 .gender(Gender.MALE)
161 .email("john.doe@example.com")
162 .phoneNumber("202-123-4678")
163 .address("33 3rd St, Smallville, KS 12333")
164 .build()
165 );
166
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
1 package com.example.lambda;
2
3 import java.util.List;
4
5 /**
6 *
7 * @author MikeW
8 */
9 public class RoboContactMethods {
10
11 public void callDrivers(List<Person> pl){
12 for(Person p:pl){
13 if (p.getAge() >= 16){
14 roboCall(p);
15 }
16 }
17 }
18
19 public void emailDraftees(List<Person> pl){
20 for(Person p:pl){
21 if (p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE){
22 roboEmail(p);
23 }
24 }
25 }
26
27 public void mailPilots(List<Person> pl){
28 for(Person p:pl){
29 if (p.getAge() >= 23 && p.getAge() <= 65){
30 roboMail(p);
31 }
32 }
33 }
34
35
36 public void roboCall(Person p){
37 System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
38 }
39
40 public void roboEmail(Person p){
41 System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
42 }
43
44 public void roboMail(Person p){
45 System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
46 }
47
48 }
As you can see from the names (callDrivers
, emailDraftees
,
and mailPilots
) the methods describe the kind of
behavior that is taking place. The search criteria is clearly
conveyed and an appropriate call is made to each robo action.
However, this design has some negatives aspects:
- The DRY principle is not followed.
- Each method repeats a looping mechanism.
- The selection criteria must be rewritten for each method
- A large number of methods are required to implement each use case.
- The code is inflexible. If the search criteria changed, it would require a number of code changes for an update. Thus, the code is not very maintainable.
Refactor the Methods
How can the class be fixed? The search criteria is a good place to start. If test conditions are isolated in separate methods, that would be an improvement.
RoboContactMethods2.java
1 package com.example.lambda;
2
3 import java.util.List;
4
5 /**
6 *
7 * @author MikeW
8 */
9 public class RoboContactMethods2 {
10
11 public void callDrivers(List<Person> pl){
12 for(Person p:pl){
13 if (isDriver(p)){
14 roboCall(p);
15 }
16 }
17 }
18
19 public void emailDraftees(List<Person> pl){
20 for(Person p:pl){
21 if (isDraftee(p)){
22 roboEmail(p);
23 }
24 }
25 }
26
27 public void mailPilots(List<Person> pl){
28 for(Person p:pl){
29 if (isPilot(p)){
30 roboMail(p);
31 }
32 }
33 }
34
35 public boolean isDriver(Person p){
36 return p.getAge() >= 16;
37 }
38
39 public boolean isDraftee(Person p){
40 return p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
41 }
42
43 public boolean isPilot(Person p){
44 return p.getAge() >= 23 && p.getAge() <= 65;
45 }
46
47 public void roboCall(Person p){
48 System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
49 }
50
51 public void roboEmail(Person p){
52 System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
53 }
54
55 public void roboMail(Person p){
56 System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
57 }
58
59 }
The search criteria are encapsulated in a method, an improvement over the previous example. The test conditions can be reused and changes flow back throughout the class. However there is still a lot of repeated code and a separate method is still required for each use case. Is there a better way to pass the search criteria to the methods?
Anonymous Classes
Before lambda expressions, anonymous inner classes were an
option. For example, an interface (MyTest.java
)
written with one test
method that returns a boolean
(a functional interface) is a possible solution. The search
criteria could be passed when the method is called. The interface
looks like this:
6 public interface MyTest<T> {
7 public boolean test(T t);
8 }
The updated robot class looks like this:
RoboContactsAnon.java
9 public class RoboContactAnon {
10
11 public void phoneContacts(List<Person> pl, MyTest<Person> aTest){
12 for(Person p:pl){
13 if (aTest.test(p)){
14 roboCall(p);
15 }
16 }
17 }
18
19 public void emailContacts(List<Person> pl, MyTest<Person> aTest){
20 for(Person p:pl){
21 if (aTest.test(p)){
22 roboEmail(p);
23 }
24 }
25 }
26
27 public void mailContacts(List<Person> pl, MyTest<Person> aTest){
28 for(Person p:pl){
29 if (aTest.test(p)){
30 roboMail(p);
31 }
32 }
33 }
34
35 public void roboCall(Person p){
36 System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
37 }
38
39 public void roboEmail(Person p){
40 System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
41 }
42
43 public void roboMail(Person p){
44 System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
45 }
46
47 }
That is definitely another improvement, because only three methods are needed to perform robotic operations. However, there is a slight problem with ugliness when the methods are called. Check out the test class used for this class:
RoboCallTest03.java
1 package com.example.lambda;
2
3 import java.util.List;
4
5 /**
6 * @author MikeW
7 */
8 public class RoboCallTest03 {
9
10 public static void main(String[] args) {
11
12 List<Person> pl = Person.createShortList();
13 RoboContactAnon robo = new RoboContactAnon();
14
15 System.out.println("\n==== Test 03 ====");
16 System.out.println("\n=== Calling all Drivers ===");
17 robo.phoneContacts(pl,
18 new MyTest<Person>(){
19 @Override
20 public boolean test(Person p){
21 return p.getAge() >=16;
22 }
23 }
24 );
25
26 System.out.println("\n=== Emailing all Draftees ===");
27 robo.emailContacts(pl,
28 new MyTest<Person>(){
29 @Override
30 public boolean test(Person p){
31 return p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
32 }
33 }
34 );
35
36
37 System.out.println("\n=== Mail all Pilots ===");
38 robo.mailContacts(pl,
39 new MyTest<Person>(){
40 @Override
41 public boolean test(Person p){
42 return p.getAge() >= 23 && p.getAge() <= 65;
43 }
44 }
45 );
46
47
48 }
49 }
This is a great example of the "vertical" problem in practice. This code is a little difficult to read. In addition, we have to write custom search criteria for each use case.
Lambda Expressions Get it Just Right
Lambda expressions solve all the problems encountered so far. But first a little housekeeping.
java.util.function
In the previous example, the MyTest
functional
interface passed anonymous classes to methods. However, writing
that interface was not necessary. Java SE 8 provides the java.util.function
package with a number of standard functional interfaces. In this
case, the Predicate
interface meets our needs.
3 public interface Predicate<T> {
4 public boolean test(T t);
5 }
The test
method takes a generic class and returns a
boolean result. This is just what is needed to make selections.
Here is the final version of the robot class.
RoboContactsLambda.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Predicate;
5
6 /**
7 *
8 * @author MikeW
9 */
10 public class RoboContactLambda {
11 public void phoneContacts(List<Person> pl, Predicate<Person> pred){
12 for(Person p:pl){
13 if (pred.test(p)){
14 roboCall(p);
15 }
16 }
17 }
18
19 public void emailContacts(List<Person> pl, Predicate<Person> pred){
20 for(Person p:pl){
21 if (pred.test(p)){
22 roboEmail(p);
23 }
24 }
25 }
26
27 public void mailContacts(List<Person> pl, Predicate<Person> pred){
28 for(Person p:pl){
29 if (pred.test(p)){
30 roboMail(p);
31 }
32 }
33 }
34
35 public void roboCall(Person p){
36 System.out.println("Calling " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getPhone());
37 }
38
39 public void roboEmail(Person p){
40 System.out.println("EMailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getEmail());
41 }
42
43 public void roboMail(Person p){
44 System.out.println("Mailing " + p.getGivenName() + " " + p.getSurName() + " age " + p.getAge() + " at " + p.getAddress());
45 }
46
47 }
With this approach only three methods are needed, one for each
contact method. The lambda expression passed to the method selects
the Person
instances that meet the test conditions.
Vertical Problem Solved
Lambda expressions solve the vertical problem and allow the easy reuse of any expression. Take a look at the new test class updated for lambda expressions.
RoboCallTest04.java
1 package com.example.lambda;
2
3 import java.util.List;
4 import java.util.function.Predicate;
5
6 /**
7 *
8 * @author MikeW
9 */
10 public class RoboCallTest04 {
11
12 public static void main(String[] args){
13
14 List<Person> pl = Person.createShortList();
15 RoboContactLambda robo = new RoboContactLambda();
16
17 // Predicates
18 Predicate<Person> allDrivers = p -> p.getAge() >= 16;
19 Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
20 Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;
21
22 System.out.println("\n==== Test 04 ====");
23 System.out.println("\n=== Calling all Drivers ===");
24 robo.phoneContacts(pl, allDrivers);
25
26 System.out.println("\n=== Emailing all Draftees ===");
27 robo.emailContacts(pl, allDraftees);
28
29 System.out.println("\n=== Mail all Pilots ===");
30 robo.mailContacts(pl, allPilots);
31
32 // Mix and match becomes easy
33 System.out.println("\n=== Mail all Draftees ===");
34 robo.mailContacts(pl, allDraftees);
35
36 System.out.println("\n=== Call all Pilots ===");
37 robo.phoneContacts(pl, allPilots);
38
39 }
40 }
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.
Resources
The NetBeans project with the source code is included in the following zip file.
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 argumentConsumer
: An action to be performed with the object passed as argumentFunction
: Transform a T to a USupplier
: Provide an instance of a T (such as a factory)UnaryOperator
: A unary operator from T -> TBinaryOperator
: 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
128 public void printWesternName(){
129
130 System.out.println("\nName: " + this.getGivenName() + " " + this.getSurName() + "\n" +
131 "Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" +
132 "EMail: " + this.getEmail() + "\n" +
133 "Phone: " + this.getPhone() + "\n" +
134 "Address: " + this.getAddress());
135 }
136
137
138
139 public void printEasternName(){
140
141 System.out.println("\nName: " + this.getSurName() + " " + this.getGivenName() + "\n" +
142 "Age: " + this.getAge() + " " + "Gender: " + this.getGender() + "\n" +
143 "EMail: " + this.getEmail() + "\n" +
144 "Phone: " + this.getPhone() + "\n" +
145 "Address: " + this.getAddress());
146 }
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
123 public String printCustom(Function <Person, String> f){
124 return f.apply(this);
125 }
126
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 Function
s defined? Here is the test
code that calls the previous method.
NameTestNew.java
9 public class NameTestNew {
10
11 public static void main(String[] args) {
12
13 System.out.println("\n==== NameTestNew02 ===");
14
15 List<Person> list1 = Person.createShortList();
16
17 // Print Custom First Name and e-mail
18 System.out.println("===Custom List===");
19 for (Person person:list1){
20 System.out.println(
21 person.printCustom(p -> "Name: " + p.getGivenName() + " EMail: " + p.getEmail())
22 );
23 }
24
25
26 // Define Western and Eastern Lambdas
27
28 Function<Person, String> westernStyle = p -> {
29 return "\nName: " + p.getGivenName() + " " + p.getSurName() + "\n" +
30 "Age: " + p.getAge() + " " + "Gender: " + p.getGender() + "\n" +
31 "EMail: " + p.getEmail() + "\n" +
32 "Phone: " + p.getPhone() + "\n" +
33 "Address: " + p.getAddress();
34 };
35
36 Function<Person, String> easternStyle = p -> "\nName: " + p.getSurName() + " "
37 + p.getGivenName() + "\n" + "Age: " + p.getAge() + " " +
38 "Gender: " + p.getGender() + "\n" +
39 "EMail: " + p.getEmail() + "\n" +
40 "Phone: " + p.getPhone() + "\n" +
41 "Address: " + p.getAddress();
42
43 // Print Western List
44 System.out.println("\n===Western List===");
45 for (Person person:list1){
46 System.out.println(
47 person.printCustom(westernStyle)
48 );
49 }
50
51 // Print Eastern List
52 System.out.println("\n===Eastern List===");
53 for (Person person:list1){
54 System.out.println(
55 person.printCustom(easternStyle)
56 );
57 }
58
59
60 }
61 }
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: bob.baker@example.com Name: Jane EMail: jane.doe@example.com Name: John EMail: john.doe@example.com Name: James EMail: james.johnson@example.com Name: Joe EMail: joebob.bailey@example.com Name: Phil EMail: phil.smith@examp;e.com Name: Betty EMail: betty.jones@example.com ===Western List=== Name: Bob Baker Age: 21 Gender: MALE EMail: bob.baker@example.com Phone: 201-121-4678 Address: 44 4th St, Smallville, KS 12333 Name: Jane Doe Age: 25 Gender: FEMALE EMail: jane.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 Name: John Doe Age: 25 Gender: MALE EMail: john.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 Name: James Johnson Age: 45 Gender: MALE EMail: james.johnson@example.com Phone: 333-456-1233 Address: 201 2nd St, New York, NY 12111 Name: Joe Bailey Age: 67 Gender: MALE EMail: joebob.bailey@example.com 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: betty.jones@example.com Phone: 211-33-1234 Address: 22 4th St, New Park, CO 222333 ===Eastern List=== Name: Baker Bob Age: 21 Gender: MALE EMail: bob.baker@example.com Phone: 201-121-4678 Address: 44 4th St, Smallville, KS 12333 Name: Doe Jane Age: 25 Gender: FEMALE EMail: jane.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 Name: Doe John Age: 25 Gender: MALE EMail: john.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333 Name: Johnson James Age: 45 Gender: MALE EMail: james.johnson@example.com Phone: 333-456-1233 Address: 201 2nd St, New York, NY 12111 Name: Bailey Joe Age: 67 Gender: MALE EMail: joebob.bailey@example.com 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: betty.jones@example.com 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.
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
1 package com.example.lambda;
2
3 import java.util.HashMap;
4 import java.util.Map;
5 import java.util.function.Predicate;
6
7 /**
8 *
9 * @author MikeW
10 */
11 public class SearchCriteria {
12
13 private final Map<String, Predicate<Person>> searchMap = new HashMap<>();
14
15 private SearchCriteria() {
16 super();
17 initSearchMap();
18 }
19
20 private void initSearchMap() {
21 Predicate<Person> allDrivers = p -> p.getAge() >= 16;
22 Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
23 Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;
24
25 searchMap.put("allDrivers", allDrivers);
26 searchMap.put("allDraftees", allDraftees);
27 searchMap.put("allPilots", allPilots);
28
29 }
30
31 public Predicate<Person> getCriteria(String PredicateName) {
32 Predicate<Person> target;
33
34 target = searchMap.get(PredicateName);
35
36 if (target == null) {
37
38 System.out.println("Search Criteria not found... ");
39 System.exit(1);
40
41 }
42
43 return target;
44
45 }
46
47 public static SearchCriteria getInstance() {
48 return new SearchCriteria();
49 }
50 }
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
11 public class Test01ForEach {
12
13 public static void main(String[] args) {
14
15 List<Person> pl = Person.createShortList();
16
17 System.out.println("\n=== Western Phone List ===");
18 pl.forEach( p -> p.printWesternName() );
19
20 System.out.println("\n=== Eastern Phone List ===");
21 pl.forEach(Person::printEasternName);
22
23 System.out.println("\n=== Custom Phone List ===");
24 pl.forEach(p -> { System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail())); });
25
26 }
27
28 }
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
9 public class Test02Filter {
10
11 public static void main(String[] args) {
12
13 List<Person> pl = Person.createShortList();
14
15 SearchCriteria search = SearchCriteria.getInstance();
16
17 System.out.println("\n=== Western Pilot Phone List ===");
18
19 pl.stream().filter(search.getCriteria("allPilots"))
20 .forEach(Person::printWesternName);
21
22
23 System.out.println("\n=== Eastern Draftee Phone List ===");
24
25 pl.stream().filter(search.getCriteria("allDraftees"))
26 .forEach(Person::printEasternName);
27
28 }
29 }
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:
=== Eastern Draftee Phone List === Name: Baker Bob Age: 21 Gender: MALE EMail: bob.baker@example.com Phone: 201-121-4678 Address: 44 4th St, Smallville, KS 12333 Name: Doe John Age: 25 Gender: MALE EMail: john.doe@example.com Phone: 202-123-4678 Address: 33 3rd St, Smallville, KS 12333
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 theList
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
10 public class Test03toList {
11
12 public static void main(String[] args) {
13
14 List<Person> pl = Person.createShortList();
15
16 SearchCriteria search = SearchCriteria.getInstance();
17
18 // Make a new list after filtering.
19 List<Person> pilotList = pl
20 .stream()
21 .filter(search.getCriteria("allPilots"))
22 .collect(Collectors.toList());
23
24 System.out.println("\n=== Western Pilot Phone List ===");
25 pilotList.forEach(Person::printWesternName);
26
27 }
28
29 }
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
10 public class Test04Map {
11
12 public static void main(String[] args) {
13 List<Person> pl = Person.createShortList();
14
15 SearchCriteria search = SearchCriteria.getInstance();
16
17 // Calc average age of pilots old style
18 System.out.println("== Calc Old Style ==");
19 int sum = 0;
20 int count = 0;
21
22 for (Person p:pl){
23 if (p.getAge() >= 23 && p.getAge() <= 65 ){
24 sum = sum + p.getAge();
25 count++;
26 }
27 }
28
29 long average = sum / count;
30 System.out.println("Total Ages: " + sum);
31 System.out.println("Average Age: " + average);
32
33
34 // Get sum of ages
35 System.out.println("\n== Calc New Style ==");
36 long totalAge = pl
37 .stream()
38 .filter(search.getCriteria("allPilots"))
39 .mapToInt(p -> p.getAge())
40 .sum();
41
42 // Get average of ages
43 OptionalDouble averageAge = pl
44 .parallelStream()
45 .filter(search.getCriteria("allPilots"))
46 .mapToDouble(p -> p.getAge())
47 .average();
48
49 System.out.println("Total Ages: " + totalAge);
50 System.out.println("Average Age: " + averageAge.getAsDouble());
51
52 }
53
54 }
And the output from the class is:
== Calc Old Style == Total Ages: 150 Average Age: 37 == Calc New Style == Total Ages: 150 Average Age: 37.5
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.
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
To navigate this Oracle by Example tutorial, note the following:
- Hide Header Buttons:
- Click the title to hide the buttons in the header. To show the buttons again, click the title again.
- Topic List:
- Click a topic to navigate to that section.
- Expand All Topics:
- Click the button to show or hide the details for the sections. By default, all topics are collapsed.
- Hide All Images:
- Click the button to show or hide the screenshots. By default, all images are displayed.
- Print:
- Click the button to print the content. The content that is currently displayed or hidden is printed.
To navigate to a particular section in this tutorial, select the topic from the list.