Shahzad Bhatti Welcome to my ramblings and rants!

April 17, 2014

Introduction to Java 8 Lambda and Stream Syntax

Filed under: Java — admin @ 11:10 pm

Introduction

Java 8 was released in March 2014 with most language-level enhancements since Java 5 back in 2004. The biggest new feature is introduction to Lambda. Lambda or Closure is a block of code that you can pass to other methods or return from methods. Previously, Java supported a form of closure via anonymous class syntax, e.g.

Java

In above example, using anonymous class could use locally defined data in method that declares it as long as it is defined with final. Here is how the example looks like with Java 8 syntax:

Java

As you can see, lambda syntax is very minimal. In addition, lambda syntax doesn’t require that you declare externally accessible data as final, though it cannot be changed. Java lambda also adds type inferencing so that you don’t have to define types of arguments. The lambda features are implemented using “invokedynamic” instruction to dispatch method calls, which was added in Java 7 to support dynamic languages. For example, let’s take a simple example:

Java

You can de-compile it using:

Shell

You will see, it generated lambda$main$0 method, e.g.

Java

You can see real byte code using:

Shell
Java

This means lambdas don’t have to keep reference of enclosing class and “this” inside lambda does not create new scope.

Types of Functions

Java 8 provides predefined functions (See http://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html), but there are four major types:

Java

The supplier method takes not arguments and produces an object, the consumer takes an argument for consumption, predicate evaluates given argument by returning true/false and function maps an argument of type T and returns an object of type R.

Supplier example

Java

Note: Java 8 provides special functions for primitive types that you can use instead of using wrapper classes for primitive types. Here is another example that shows how you can write efficient log messages:

Java

Consumer example

Java

Predicate example

Java

In addition to test method, you can also use and, negate, or method to combine other predicates.

Function example

Java

Custom Functions

In addition to predefined functions, you can define your own interface for functions as long as there is a single method is declared. You can optionally declare interface with @FunctionalInterface annotation so that compiler can verify it, e.g.

Java

Method Reference

In addition to passing lambda, you can also pass instance or static methods as closures using method reference. There are four kinds of method references:

  • Reference to a static method ContainingClass::staticMethodName
  • Reference to an instance method of a particular object ContainingObject::instanceMethodName
  • Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
  • Reference to a constructor ClassName::new

Streams

In addition to lambda support, Java 8 has updated collection classes to support streams. Streams don’t really store anything but they behave as pipes for computation lazily. Though, collections have limited size, but streams can be unlimited and they can be only consumed once. Streams can be accessed from collections using stream() and parallelStream() methods or from an array via Arrays.stream(Object[]). There are also static factory methods on the stream classes, such as Stream.of(Object[]), IntStream.range(int, int), etc. Common intermediate methods using as pipes that you can invoke on streams:

  • filter()
  • distinct()
  • limit()
  • map()
  • peek()
  • sorted()
  • unsorted()

In above examples sorted, distinct, unsorted are stateful, whereas filter, map, limit are stateless. And here are terminal operations that trigger evaluation on streams:

  • findFirst()
  • min()
  • max()
  • reduce()
  • sum()

You can implement your own streams by using helper methods in StreamSupport class.

Iterating

Java 8 streams support forEach method for iterating, which can optionally take a consumer function, e.g.

Java

Parallel iteration:

Java

Note that by default iterating parallel stream would be unordered but you can force ordered iteration using forEachOrdered method instead of forEach.

Filtering

We already saw predicate functions and filtering support in streams allow extract elements of collections that evaluates true to given predicate. Let’s create a couple of classes that we will use later:

Java

Now let’s create a class that will filter games by types:

Java

As you can see, filter can accept lambda or method reference.

Map

Map operation on streams applies a given function to transform each element in stream and produces another stream with transformed elements.

Java

Note that flatMap takes collection of objects for each input and flattens it and produces a single collection. Java streams also produces map methods for primitive types such as mapToLong, mapToDouble, etc.

Sorting

Previously, you had to implement Comparable interface or provide Comparator for sorting but you can now pass lambda for comparison, e.g.

Java

Min/Max

Java streams provide helper methods for calculating min/max, e.g.

Java

Reduce/Fold

Reduce or fold generalizes the problem where we compuate a single value from collection, e.g.

Java

Grouping/Partitioning

groupingBy method of Collectors allows grouping collection, e.g.

Java

partitioningBy groups collection into two collection, e.g.

Java

String joining

Here is an example of creating a string from collection:

Java

Lazy Evaluation

Java Map interface now supports lazy evaluation by adding object to Map if the key is not present:

Java

Parallel Streams

Though, streams processes elements serially but you can change stream() method of collection to parallelStream to take advantage of parallel processing of stream. Parallel stream use ForkJoinPool by default and use as many threads as you have processors (Runtime.getRuntime().availableProcessors()), e.g.

Java

If you need to customize thread pool size, you can create parallel stream inside fork-join-pool, e.g.

Java

Default methods and Mixins

For anyone who has to support interfaces for multiple clients knows the frustration of adding new methods because it requires updating all clients. You can now add default methods on interfaces and add static methods, e.g.

Java

Optional

Tony Hoare called nulls a billion dollar mistake and Java has an infamous NullPointerException error when you dereference a null object. Now you can get rid of those nasty errors using Optional, which acts as Maybe monads in other languages, e.g.

Java

CompletableFuture

Java has long supported future, but previously you had to call blocking get() to retrieve the result. With Java 8, you can use CompletableFuture to define the behavior when asynchronous processing is completed, e.g.

Java

Summary

Over the past few years, functional programming languages have become mainstream and Java 8 brings many of those capabilities despite being late. These new features help write better and more concise code. You will have to change existing code to make more use of immutable objects and use streams instead of objects when possible. As far as stability, I found java 8 compiler on Linux environment a bit buggy that crashed often so it may take a little while before Java 8 can be used in production.

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

You must be logged in to post a comment.

Powered by WordPress