Friday, 13 October 2017

Java 8 | forEach() method in Iterable and Stream interface

Java 8 has introduced forEach(), a new way to loop over a List or Collection, by using the forEach() method of the new Stream class. It is defined in Iterable and Stream interface. It is a default method defined in the Iterable interface.

default void forEach(Consumer<? super T> action) {

    Objects.requireNonNull(action);

    for (T t : this) {

        action.accept(t);

    }

}

Collection classes which extend Iterable interface can use a forEach loop to iterate elements. This method takes a single parameter which is a functional interface so it can be passed t lambda expression as an argument.

Before Java8, we need to create an Iterator to traverse through a Collection. We need to create an Iterator whose whole purpose is to iterate over and then we have business logic in a loop for each of the elements in the Collection. We might get ConcurrentModificationException if the iterator is not used properly.

Java 8 has introduced the forEach method which helps us to focus on business logic only. forEach method takes java.util.function.Consumer object as an argument, so it helps in having our business logic at a separate location that we can reuse.
Example:
package com.java8;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.lang.Integer;

/**
 * This class contains the Java8 forEach loop.
 *
 * @author algos.debug
 */
public class Java8ForEach {

     public static void main(String[] args) {

           // creating sample Collection
           List<Integer> myList = new ArrayList<Integer>();
           for (int i = 0; i < 10; i++) {
                myList.add(i);
           }

           /* traversing using Iterator */
           System.out.println("\nIterator Value ");
           Iterator<Integer> it = myList.iterator();
           while (it.hasNext()) {
                Integer i = it.next();
                System.out.print(" " + i);
           }

           System.out.println("\nforEach anonymous class Value ");
           /* traversing through forEach method of Iterable with anonymous class. */
           myList.forEach(new Consumer<Integer>() {
                public void accept(Integer t) {
                     System.out.print(" " + t);
                }
           });

           System.out.println("\nforEach with Consumer interface implementation ");
           /* traversing with Consumer interface implementation. */
           MyConsumer action = new MyConsumer();
           myList.forEach(action);

           System.out.println("\nforEach with Lambda ");
           /* Traversing forEach with Lambda. */
           myList.forEach((a) -> {
                System.out.print(" " + (a));
           });

     }

}

/**
 * Consumer implementation that can be reused.
 *
 * @author algos.debug
 */
class MyConsumer implements Consumer<Integer> {
     public void accept(Integer t) {
           System.out.print(" " + t);
     }

}

One more thing to remember about the forEach() method is that it's a terminal operation, which means you cannot reuse the Stream after calling this method. It will throw IllegalStateException if you try to call another method on this Stream.
package com.java8;

import java.util.Arrays;
import java.util.stream.Stream;

/**
 * @author algos.debug
 *
 */
public class ForeachStreamException {

     public static void main(String[] args) {

           String[] array = { "a", "b", "c", "d", "e" };
           Stream<String> stream = Arrays.stream(array);

           /** loop a stream .*/
           stream.forEach(x -> System.out.print(" "+x));

           System.out.println();
          
           /** Reuse it to filter again, throws IllegalStateException .*/
           long count = stream.filter(x -> "b".equals(x)).count();
           System.out.println(count);
     }
}

Output:
      a b c d e
      Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
          at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
          at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)

Advantages of forEach() over for traditional  loop in Java 8 
1.     More succinct/crisp code, sometimes just one-liner. Sometimes the number of lines might increase but the forEach method helps in having the logic for iteration and business logic at separate place resulting in higher separation of concern and cleaner code.
2.      It can be passed lambda expression, which gives us the immense flexibility to change in the loop.
3.      forEach looping can be made parallel with minimal effort e.g. without writing a single line of concurrent code, all we need to do is call parallelStream() method. 

Important points about forEach() method
1.      forEach() method is defined at two places, on Iterable interface as well as on Stream class. So we can use with Collection and streams.
Example: list.forEach() and list.stream.forEach
2.      forEach() should be preferred with streams because streams are lazy and not evaluated until a terminal operation is called. 
3.      forEach() is a terminal operation, Calling any method on stream after this will lead to IllegalStateException.
4.      When you run forEach() method on parallel stream the order on which elements are processed is not guaranteed, though you can use forEachOrdered() to impose ordering.
5.      forEach() method accepts a Consumer instance, which is a functional interface so It can be passed to lambda expression. 

Tuesday, 10 October 2017

Java 8 | Usage of Java Lambda Expressions

Lambda expression is a new and important feature of Java which was included in Java SE 8. It provides a clear and concise way to represent one method interface using an expression (Function interface).
It helps to iterate, filter (using streams) and extracts data from the collection. Before lambda expression, the anonymous inner class was the only option to implement the method.

Syntax
A lambda in Java essentially consists of three parts: a parenthesized set of parameters, an arrow, and then a body, which can either be a single expression or a block of Java code.
(Set of parameters) -> {body}

Set of parameters: It can be empty or non-empty as well.
Arrow-token: It is used to link arguments-list and body of expression.
Body: Contains expressions and statements for lambda expression.

Java Example without Lambda Expression
package com.java8;
interface Addition {
     public void add(int a, int b);
}

public class LambdaExpression {
     public static void main(String[] args) {

           /** Without lambda, Addition implementation using anonymous class .*/
           Addition d = new Addition() {
                public void add(int a, int b) {
                     System.out.println("Sum is" + (a + b));
                }
           };
           d.add(3, 5);
     }
}

Output: Sum is 8

Java Example with Lambda Expression
package com.java8;
interface Addition {
     public void add(int a, int b);
}

public class LambdaExpression {
     public static void main(String[] args) {

           /** Addition implementation using lambda expression.*/
           Addition d = (int a, int b)-> {
                System.out.println("Lambda | Sum is "+(a+b));
           };
           d.add(3, 5);
     }
}
Output: Lambda | Sum is 8

Few examples of Lambda expression
package com.java8;
interface Hello {
     public String sayHello();
}

public class HelloLambda {
     public static void main(String[] args) {
           Hello hello = () -> {
                return "Hello! I am Lambda without parameter.";
           };
           System.out.println(hello.sayHello());
     }
}
Output: Hello! I am Lambda without parameter.

Java Lambda Expression with Single Parameter
package com.java8;
interface Hello {
     public String sayHello(String name);
}

public class HelloLambda {
     public static void main(String[] args) {
           Hello hello = (String name) -> {
                return "Hello "+name +"! I am Lambda with Single parameter.";
           };
           System.out.println(hello.sayHello("Coder"));
     }
}
Output: Hello Coder! I am Lambda with Single parameter.

Java Lambda Expression with Multiple Parameters
package com.java8;
interface Addition {
     public void add(int a, int b);
}

public class LambdaExpression {
     public static void main(String[] args) {

           /** Without lambda, Addition implementation using anonymous class .*/
           Addition d = new Addition() {
                public void add(int a, int b) {
                     System.out.println("Sum is" + (a + b));
                }
           };
           d.add(3, 5);
     }
}

Output: Sum is 8

Java Lambda Expression with or without return keyword
If there is only one statement, you may or may not use return keyword. You must use return keyword when lambda expression contains multiple statements.
package com.java8;
interface Addition {
     public int add(int a, int b);
}

public class LambdaExpression {
     public static void main(String[] args) {

           /** Multiple statement required the return type. */
           Addition mulAdd = (int a, int b) -> {
                return (a + b);
           };

           System.out.println("Multiple statement: Sum is " + mulAdd.add(3, 5));
          
           /** Single statement, return type not required. */
           Addition singAdd = (int a, int b) ->  (a + b);

           System.out.println("Single statement: Sum is " + singAdd.add(3, 5));
     }
}
Output:
Multiple statement: Sum is 8
Single statement: Sum is 8

Foreach Loop using Java Lambda expression
package com.java8;
import java.util.ArrayList;
import java.util.List;

public class LambdaForEach {

     public static void main(String[] args) {

           List<String> list = new ArrayList<String>();
           list.add("Algos");
           list.add("Paras");
           list.add("Sattu");

           /* For each for Lambda expression. */
           list.forEach((n) -> System.out.println(n));
     }
}
Output:
Algos
Paras
Sattu


Create Thread using Lambda expression
package com.java8;
public class LambdaThread {
     public static void main(String[] args) {
          
           /** Thread Example without lambda. */
           Runnable r1 = new Runnable() {
                public void run() {
                     System.out.println("Thread1 is running...");
                }
           };
           Thread t1 = new Thread(r1);
           t1.start();
          
           /** Thread Example with lambda .*/
           Runnable r2 = () -> {
                System.out.println("Thread2 is running...");
           };
           Thread t2 = new Thread(r2);
           t2.start();
     }
}
Output:
Thread1 is running...
Thread2 is running...



As per the definition of Java lambda expression, it can be used in the collection framework. It provides the efficient and concise way to iterate, filter and fetch data. Check the below implementation for the same:


Lambda to implement the Comparator

package com.java8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Employee {
     int empId;
     String name;

     public Employee(int empId, String name) {
           this.empId = empId;
           this.name = name;
     }
}

public class LambdaComparator {
    
     public static void main(String[] args) {
          
           List<Employee> employees = new ArrayList<Employee>();

           /** Adding Products. */
           employees.add(new Employee(1, "Algos"));
           employees.add(new Employee(3, "Paras"));
           employees.add(new Employee(2, "Deshraj"));

           System.out.println("Sorting on the basis of Employee Name...");

           /** Implementing lambda expression .*/
           Collections.sort(employees, (p1, p2) -> {
                return p1.name.compareTo(p2.name);
           });
          
           for (Employee p : employees) {
                System.out.println(p.empId + " " + p.name);
           }
     }
}
Output:
Sorting on the basis of Employee Name...
1 Algos
2 Deshraj
3 Paras

Filter Collection Data using Streams & Lambda

package com.java8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

class Employee {
     int empId;
     String name;
     long salary;

     public Employee(int empId, String name, long salary) {
           this.empId = empId;
           this.name = name;
           this.salary = salary;
     }
}

public class LambdaComparator {
    
     public static void main(String[] args) {
          
           List<Employee> employees = new ArrayList<Employee>();

           /** Adding Employees. */
           employees.add(new Employee(1, "Algos", 130000L));
           employees.add(new Employee(3, "Paras", 100000L));
          employees.add(new Employee(2, "Deshraj", 80000L));

           System.out.println("Filter the employee have salary>=100000 ");

           /** using lambda to filter data. */
        Stream<Employee> filtered_emp = employees.stream().filter(p -> p.salary >= 100000); 
         
        // using lambda to iterate through collection 
        filtered_emp.forEach( 
                employee -> System.out.println(employee.name+": "+employee.salary
        ); 
     }
}

Output:
     Filter the employee have salary>=100000
     Algos: 130000
     Paras: 100000


Lambda for Event Listener example


package com.java8;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class LambdaEventListener {
     public static void main(String[] args) {
           JTextField tf = new JTextField();
           tf.setBounds(50, 50, 150, 20);
           JButton b = new JButton("click");
           b.setBounds(80, 100, 70, 30);

           /** lambda expression implementing here. */
           b.addActionListener(e -> {
                tf.setText("hello! Swing");
           });

           JFrame f = new JFrame();
           f.add(tf);
           f.add(b);
           f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           f.setLayout(null);
           f.setSize(300, 200);
           f.setVisible(true);
     }
}
Related Posts Plugin for WordPress, Blogger...