Tuesday, 4 October 2016

Java Lambda Expressions


Java lambda expressions are Java's first step into functional programming. A Java lambda expression is thus a function which can be created without belonging to any class. A lambda expression can be passed around as if it was an object and executed on demand.
It provides a clear and concise way to represent one method interface using an expression. It is very useful in the collection library as it helps to iterate, filter and extract data from the collection.

Before lambda expression, the anonymous inner class was the only option to implement the method. Now we can replace the java inner anonymous class using Lambda expressions.
Java lambda expression is treated as a function, so the compiler does not create an extra .class file, unlike Anonymous class.

What can we achieve using Lambda expression?
Using lambda expression, sequential and parallel execution can be achieved by passing behavior into methods. In the Java world, lambdas can be thought of as an anonymous method with a more compact syntax. Here compact means that it is not mandatory to specify access modifiers, return type, and parameter types while defining the expression.

Why Lambdas in Java?
There are various reasons for the addition of lambda expression in Java platform but the most beneficial of them is that we can easily distribute processing of collection over multiple threads. Prior to Java 8, if the processing of elements in a collection had to be done in parallel, the client code was supposed to perform the necessary steps and not the collection. In Java 8, using lambda expression and Stream API we can pass processing logic of elements into methods provided by collections and now the collection is responsible for the parallel processing of elements and not the client.

Also, parallel processing effectively utilizes multicore CPUs used nowadays.

Syntax:
(parameters) -> expression
or
(parameters) -> { statements; }

Java 8 provides support for lambda expressions only with functional interfaces. As there is only one abstract method in Functional Interface, there is no confusion in applying the lambda expression to that method.

Benefits of Lambda Expression
1. Fewer Lines of Code
     /** Runnable using anonymous class. */
     Runnable r1 = new Runnable() {
           @Override
           public void run() {
                System.out.println("Anonymous class !");

           }
     };

     /** Runnable using Lambda expression. */
     Runnable r2 = () -> {
           System.out.println("Lambda expression!");
     };
·        As Java lambda can be used only with functional interfaces. In the above example, Runnable is a functional interface, so we can easily apply lambda expression here.
·         In this case, we are not passing any parameter in lambda expression because the run() method of the functional interface (Runnable) takes no argument.
·         Also, the syntax of the lambda expression says that we can omit curly braces ({}) in case of a single statement in the method body. In the case of multiple statements, we should use curly braces as done in the above example.

2. Sequential and Parallel Execution Support by passing behavior in methods
Another benefit of using lambda expression is that we can benefit from the Stream API sequential and parallel operations support.

To explain this, let’s write a method to check whether a number is a prime number or not.

     /** Traditional approach. */
     private static boolean isPrime(int number) {
           if (number < 2) {
                return false;
           }
           for (int i = 2; i < number; i++) {
                if (number % i == 0) {
                     return false;
                }
           }
           return true;
     }

The problem with above code is that it's sequential in nature if the number is very huge then it will take a significant amount of time. Another problem with code is that there are so many exit points and it's not readable.

     /** Declarative approach. */
     private static boolean isPrime(int number) {
           return number > 1 && IntStream.range(2, number).noneMatch(
                                index -> number % index == 0);
     }
    
     private static boolean isPrime(int number) {
           IntPredicate isDivisible = index -> number % index == 0;
           return number > 1 && IntStream.range(2, number).noneMatch(isDivisible);
     }

IntStream is a sequence of primitive int-valued elements supporting sequential and parallel aggregate operations. This is the int primitive specialization of Stream.

3. Passing Behaviors into methods
Let's see how we can use lambda expressions to pass behavior of a method with a simple example. Let's say we have to write a method to sum the numbers in a list if they match given criteria. We can use Predicate and write a method like below.
     public static int sumWithCondition(List<Integer> numbers,
                Predicate<Integer> predicate) {
           return numbers.parallelStream().filter(predicate).mapToInt(i -> i).sum();
     }
4. Higher Efficiency (Utilizing Multicore CPU’s)
Using Stream API and lambda expression we can achieve higher efficiency (parallel execution) in case of bulk operations on collections. Also, the lambda expressions can help in achieving internal iteration of collections rather than external iteration as shown in the above example. As nowadays we have CPUs with multicores, we can take advantage of these multi-core CPUs by parallel processing of collections using lambda.

Lambda Expression and Objects
In Java, any lambda expression is an object as is an instance of a functional interface. We can assign a lambda expression to any variable and pass it like any other object.
See the example below on how a lambda expression is assigned to a variable, and how it is invoked.
@FunctionalInterface
public interface NumComparator {
     public boolean compareNumbers(int a1, int a2);
}

public class AgeComparatorImpl {
     public static void main(String[] args) {
           NumComparator comparator = (int a1, int a2) -> {
                return a1 > a2;
           };
           boolean result = comparator.compareNumbers(10, 20);
           System.out.println(result);
     }
}

Where you can use Lambda expressions?
Lambda expressions can be used anywhere in Java 8 where we have a target type. In Java, we have target type in the following contexts
·         Variable declarations and assignments
·         Return statements
·         Method or constructor arguments.
Related Posts Plugin for WordPress, Blogger...