Tuesday, September 27, 2016

Lambda Expressions Java 8 or Java Lambda Expressions


Lambda Expressions Java 8 ( or Closures)

There are several ways lambda expressions can be written.I went through several blogs and also some Oracle docs.The outcome of that are these samples which shows how to use Lambda expressions.
Java 8 supports functional programming

Lambda expression or Closures allows functional programming, and simplifies the development a lot. A lambda expression is characterized by the following syntax - (parameter) -> expression body.
Lambdas work only on Single Method Abstraction (SAM).
i.e. interfaces which have just one method. Anonymous class can implement any.

Here is an example of anonymous class (java 7) and
same thing which can be written even more easily with Java 8 using lambda.

/* 
 You can read more for difference between Annonymos class and Lambda.
 http://stackoverflow.com/questions/22637900/java8-lambdas-vs-anonymous-classes
 
*/
public class AnonymousClass2 {

 public static void main(String[] args) {
  Thread t = new Thread(runnable1);
  t.start();
  t = new Thread(runnable2);
  t.start();
 }
 
 /*
  * Old way without Lambda: write entire stuff here
  */
 static Runnable runnable1 = new Runnable() {
  @Override
  public void run() {
   System.out.println("Running without Lambda");
  }
 };
 
 
 /* With Lambda one line. Lambdas work only on Single Method Abstraction (SAM). 
  *i.e. interfaces which have just one method. Anonymous class can implement any. */
 
 static Runnable runnable2 = () -> { System.out.println("Running from Lambda"); };
}


Using Lambda Expression for Sorting (Without Predicates) using Collections.sort(.....).
For each call to Collections sort() method we will pass an expression.
There are several ways we can express this.
For this we will use Employee  java object as described here .

package com.rama.jdk8.closures;
import java.util.Date;

public class Employee {
 
 private String fName;
 private String lName;
 private Date date;
 
 public Employee(String fName,String lName,Date date){
  this.fName = fName;
  this.lName = lName;
  this.date = date;
 }
 //....
 // Generate Getters and Setters. Left it intentionally 
 
 @Override
 public String toString() {
  StringBuilder sbud = new StringBuilder();
  sbud.append(" fName="+ fName);
  sbud.append(" lName="+ lName);
  sbud.append(" date="+ date +"\n");
  return sbud.toString();
 }
 
}

Here is the class that shows  the following flavors of sorting.
i.e Sorting using

  • Comparator.comparing(Employee::getfName)
  • By Passing lambda expression (e1,e2)-> e1.getfName().compareTo(e2.getfName())
  • Reversing the order after Sorting using "comparator.reversed()"
  • Nesting of Comparators like Comparator.comparing(Employee::getfName).thenComparing(Employee::getlName);
  • Using Stream and difference stream() and ParealleStream() on a collection.
All the above mentioned are covered in this class. Try experimenting more by commenting and uncommenting the code.

package com.rama.jdk8.closures;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author TWreddy
 *  This is very basic example of how to use Lambdas for sortings. 
 *  Later I will show how to use Predicates to do the sorting
 *  For all this examples we will use Employee.java which is POJO with few setters and getters 
 */
public class EmployeeSortLambda {
 
 public static void main(String[] args) {
 /* Comment or Uncomment the method you want to run  */
  //sortByFirstName();
  //sortByLastName();
  //sortByDob();
  //getByLastName("Reddy");
  //sortEmployeesByFname();
  //sortEmployeesByFnameinRevereseOrder();
  //sortEmployeesByFnameinRevereseOrderAndThenByLastName();
  //parallelSorting();
  printAll();
 }
 
 public static void  sortByFirstName(){
   List<Employee> employeeList = getEmployees();
   //Collections.sort(employeeList,(e1,e2)-> e1.getfName().compareTo(e2.getfName()));
   //Easier way 
   Collections.sort(employeeList,Comparator.comparing(Employee::getfName));
   System.out.println(employeeList);
  }
  
  public static void  sortByLastName(){
   List<Employee> employeeList = getEmployees();
   Collections.sort(employeeList, (e1, e2) -> { return e1.getlName().compareTo(e2.getlName());});
   System.out.println("Sorted by Last Name:\n"+ employeeList);
   }
  
  
  public static void  sortByDob(){
   List<Employee> employeeList = getEmployees();
   
   Collections.sort(employeeList, (e1, e2) -> { 
    return e1.getDate().compareTo(e2.getDate());
   });
   
   System.out.println("Sorted by Date:\n"+ employeeList);
   }
  
  public static void  getByLastName(String lastName){
   List<Employee> employeeList = getEmployees();
   
   List<Employee> employeeByLastName = new ArrayList<Employee>();
   employeeList.forEach((emp) -> {
    if(emp.getlName().equalsIgnoreCase(lastName)){
     employeeByLastName.add(emp);
    }
   });
   
   System.out.println(" Employee by Last name=\n"+ employeeByLastName);
   }
  
  public static void sortEmployeesByFname(){
   List<Employee> employees  = getEmployees();
  //Sort all employees by first name
  employees.sort(Comparator.comparing(e -> e.getfName()));
  //OR you can use below
  employees.sort(Comparator.comparing(Employee::getfName));
  
  System.out.println("Sorty by Fname:\n"+ employees);
      
  }
  
  /**
  *  Reversiong the SOrt order 
  */
 public static void sortEmployeesByFnameinRevereseOrder(){
   List<Employee> employees  = getEmployees();
  //Sort all employees by first name
   Comparator<Employee> comparator = Comparator.comparing(e -> e.getfName());
   employees.sort(comparator.reversed());
   System.out.println("Sort by Fname but in reverse Order:\n"+ employees);
      
  }
  
  /**
  *  Nesting of Comparators to sort using Lambda
  */
 public static void sortEmployeesByFnameinRevereseOrderAndThenByLastName(){
   List<Employee> employees  = getEmployees();
  //Sort all employees by first name
  Comparator<Employee> groupByComparator = Comparator.comparing(Employee::getfName).thenComparing(Employee::getlName);
  employees.sort(groupByComparator);
  System.out.println("Sort by Fname and then by Lname:\n"+ employees);
      
  }
 
  /**
   * Use only if the collections is large else go with normal way
   * Sorting in Parallel.
   **/
  public static void parallelSorting(){
   
   List<Employee> employees  = getEmployees();
   //Sort all employees by first name
   Comparator<Employee> groupByComparator =       Comparator.comparing(Employee::getfName).thenComparing(Employee::getlName);
   //employees.stream().parallel().sorted(groupByComparator).collect(Collectors.<Employee>toList());
   employees.parallelStream().sorted(groupByComparator).collect(Collectors.<Employee>toList());
   System.out.println("Parallel sorting using "+ employees);
  }
  
  /**
  *  Lets say you want to print every Object in the List 
  */
 public static void printAll(){
   List<Employee> employees  = getEmployees();
   //employees.stream().forEach(System.out::println);
   //or
   Consumer<Employee> printEmp= (emp)-> System.out.println(emp);
   employees.stream().forEach(printEmp);
  }
 
  
  public static List<Employee> getEmployees(){
   List<Employee> employeeList = new ArrayList<Employee>();
   
   Employee e1 = new Employee("RamaChandra","Reddy",new Date("11/23/1990"));
   Employee e2 = new Employee("John","Smith",new Date("06/06/1975"));
   Employee e3 = new Employee("Douglas","Whitman",new Date("11/22/1974"));
   Employee e4 = new Employee("Albert","Einstien",new Date("01/01/1950"));
   Employee e5 = new Employee("Rama","Krishnan",new Date("06/14/1975"));
   Employee e6 = new Employee("Sanjeev","Reddy",new Date("01/01/1983"));
   
  
   employeeList.add(e1);
   employeeList.add(e2);
   employeeList.add(e3);
   employeeList.add(e4);
   employeeList.add(e5);
   employeeList.add(e6);
   
   
   return employeeList;
  }
}

Passing Function as Parameter using Lambda Expression

Ever wondered how to pass a function as parameter. We can do that with Java Lambda expressions
Here is an example.

/**
 * 
 * @author twreddy
 * How to use  Function as prameter in Lambda
 **/
public class LambdaFunctionArgument {

 interface Circle {
  double get(double radius); /// Can be area or circumeference...etc.
 }

 public double circleOperation(double radius, Circle c) {
  return c.get(radius);
 }
 
 public static void main(String args[]){
  LambdaFunctionArgument reference = new LambdaFunctionArgument();
  //Circle.get() implementation to find area....
  Circle circleArea =  (double r) -> {  return (Math.PI * r *r);}; 
  //Circle.get() implementation to calculate circumference... 
  Circle circleCircumference =  (double r)->{return (2 * Math.PI * r);}; 
  double area = reference.circleOperation(10, circleArea);
  double circumference = reference.circleOperation(10, circleCircumference);
 
  System.out.println("Area: "+area+" . Circumference: "+circumference);
 }
}
Using Java 8 Predefined functional interfaces (see java.util.function.* package ) provided as part of  Java 8. All interfaces in this package are annotated with @FunctionalInterface  and have Single Abstract Method (SAM) .
Here is one example you can try others.

import java.util.function.DoubleBinaryOperator;

public class MathOperationLambda {
 
 public static void main(String[] args) {
  
  MathOperation addOperation = (a,b)->{ return a+b;};
  MathOperation subtractOperation = (a,b)->{ return a-b;};
  MathOperation divideOperation = (a,b)->{ return a/b;};
  MathOperation multiplyOperation = (a,b)->{ return a*b;};
  
  float a =10, b=5;
  System.out.println(addOperation.operation(a,b));
  System.out.println(subtractOperation.operation(a,b));
  System.out.println(divideOperation.operation(a,b));
  System.out.println(multiplyOperation.operation(a,b));
  
  
  //Or you can use predefined functional interface 
  DoubleBinaryOperator addOp = (x,y)->{ return x+y;};
  System.out.println(addOp.applyAsDouble(a,b));
  
 }
 
}

interface MathOperation{
 public float operation(float a, float b);
}



Java 8 Streams:

Supports functional-style operations on streams of elements, such as map-reduce transformations n collections. Use stream operations to express sophisticated data processing queries.
Typical processing patterns on collections are similar to SQL-like operations such as “finding” (a highest value or lowest value)  or “grouping”  or doing count...etc (More like SQL operations on Collection and more..)

Here is one example of using stream()  vs  parallelStream()

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author twreddy
 *   Good example showing usage of stream() and parallelStream() and the difference 
 *   And also some new methods thats added to Map class.
 */
public class SequentialParallelSort {
 
 public static void main(String[] args) {
  List<String> values = getData();
  
  parallelSort(values);
  sequentialSort(values);
 }
 
 public static void parallelSort(List<String> values){
  long t0 = System.nanoTime();
  long count = values.parallelStream().sorted().count();
  System.out.println(count);
  long t1 = System.nanoTime();
  long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
  System.out.println(String.format("parallel sort took: %d ms", millis));
 }
 
 public static void sequentialSort(List<String> values){
  long t0 = System.nanoTime();
  long count = values.stream().sorted().count();
  System.out.println(count);
  long t1 = System.nanoTime();
  long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
  System.out.println(String.format("sequential sort took: %d ms", millis));
 }
 
 public static List<String> getData(){
  int max = 1000000;
  List<String> values = new ArrayList<>(max);
  for (int i = 0; i < max; i++) {
      UUID uuid = UUID.randomUUID();
      values.add(uuid.toString());
  }
  return values;
 }
 
}


JDK 8 Optional:  How to handle/avoid  Nullpointer Exceptions ?
=======================================================
JDK 8 has java.util.Optional which can be used (It can avoid Null pointer Exception in most cases, yes not all cases)

Here is one simple example.

Another great example at 
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html


import java.util.Optional;
public class OptionalTest {

 public static void main(String[] args) {
  
  Optional<Integer> canBeEmpty1 = Optional.of(5);
  System.out.println(canBeEmpty1.isPresent());                    // returns true
  System.out.println(canBeEmpty1.get());                          // returns 5

  Optional<Integer> canBeEmpty2 = Optional.empty();
  System.out.println(canBeEmpty2.isPresent());                    // returns false
  
  Optional<String> optional = Optional.of("bam");
  System.out.println(optional.isPresent());           // true
  System.out.println(optional.get());                 // "bam"
  System.out.println(optional.orElse("fallback"));    // "bam"
  optional.ifPresent((s) -> System.out.println(s.charAt(0)));     
 }

}

Java 8 Predicates: 

This is a functional interface and can therefore be used as the assignment target for a lambda
expression or method reference.You can use them anywhere where you need to evaluate a condition
on group/collection of similar objects such that evaluation can result either in true or false

@FunctionalInterface
public interface Predicate<T>{
....
}


Lets use our previous Employee object 
public class Employee {
    
    private Integer id;
    private Integer age;
    private String gender;
    private String firstName;
    private String lastName;
    
    public Employee(Integer id, Integer age, String gender, String fName, String lName){
        this.id = id;
        this.age = age;
        this.gender = gender;
        this.firstName = fName;
        this.lastName = lName;
    }
      
          //...
  //... Generate Getters and Setters.....Please.
 
  @Override
     public String toString() {
         return this.id.toString()+" - "+this.age.toString() +" Gender:"+ this.getGender() +"\n"; 
     }
}
//Here are the Predicates we will define to SORT.

Also you will see that we have used a method called flatMap() and there is another method map() which is not mentioned in this example.
Both map and flatMap can be applied to a Stream<T> and they both return a Stream<R>. The difference is that the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value.


import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/** 
 * Lets define the Predicates which we will use for Sorting the Employees collection.
 */
public class EmployeePredicates {
 
 public static Predicate<Employee> isAdultMale() {
  return emp -> emp.getAge() > 21 && emp.getGender().equalsIgnoreCase("M");
  
 }
 
  public static Predicate<Employee> isAdultFemale(){
   return emp -> emp.getAge() > 21 && emp.getGender().equalsIgnoreCase("F");
  }
  
  public static Predicate<Employee> isAgeMoreThan(Integer age){
   return emp-> emp.getAge() > age;
  }
  
  public static List<Employee> filterEmployees(List<Employee>  employees,Predicate<Employee> predicate){
   return employees.stream().filter(predicate).collect(Collectors.<Employee>toList());
  }
}
Now lets put the Predicates we defined to test. (Also compare against the sorting technique we used earlier using Lambda expression on employee collection without Predicates)
You clearly see this more cleaners.

mport static com.rama.jdk8.predicate.EmployeePredicates.*;
public class EmployeePredicateTest {
 
 
 public static void main(String[] args) {
  
         Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
         Employee e2 = new Employee(2,13,"F","Martina","Hengis");
         Employee e3 = new Employee(3,43,"M","Ricky","Martin");
         Employee e4 = new Employee(4,26,"M","Jon","Lowman");
         Employee e5 = new Employee(5,50,"F","Cristine","Maria");
         Employee e6 = new Employee(6,15,"M","David","Feezor");
         Employee e7 = new Employee(7,68,"F","Melissa","Roy");
         Employee e8 = new Employee(8,79,"M","Alex","Gussin");
         Employee e9 = new Employee(9,15,"F","Neetu","Singh");
         Employee e10 = new Employee(10,45,"M","Naveen","Jain");
         
         List<Employee> employeeList = new ArrayList<Employee>();
         employeeList.addAll(Arrays.asList(e1,e2,e3,e4,e5,e6,e7,e8,e9,e10));
         System.out.println(employeeList);
         
          
         System.out.println("Male Adults="+ filterEmployees(employeeList,isAdultMale()));
System.out.println("Not Female Adults="+ filterEmployees(employeeList,isAdultFemale().negate()));
         System.out.println("Female Adult="+ filterEmployees(employeeList,isAdultFemale()));
         System.out.println("Age > 40 ="+ filterEmployees(employeeList,isAgeMoreThan(40)));
         System.out.println("Adult male and age > 40="+ filterEmployees(employeeList,isAdultMale().and(isAgeMoreThan(40))));
         
 }
}

Further Examples of Lambda :

Here is order of operations you can apply on collections  once you obtain data as Stream 


List<Integer> empIds = 
    employeeList.parallelStream()
                .filter(isAdultMale())
                .sorted(comparing(Employee::getId).reversed())
                .map(Employee::getId)
                .collect(toList());
Using Lambda expression you can simulate functionality like 
- "ORACLE LIKE function." - We can use  filter(....)
- "GROUP BY like in oracle." - We can use Collectors.groupingBy(...)
          
More examples..

For this lets use a DTO called Article.java as below.

 public class Article {
 private  String title;
 private  String author;
 private  List<String> tags = new ArrayList<String>();
 
 public Article(){
  
 }
 
 public Article(String title,String author, List<String> tags){
  this.title = title;
  this.author = author;
  this.tags.addAll(tags);
 }
 
 //.....
 //... Please generate getters and Setters. 
 @Override
 public String toString() {
  StringBuilder sbud = new StringBuilder();
  sbud.append("title="+title);
  sbud.append(" author="+author);
  sbud.append(" tags="+ tags.toString());
  return sbud.toString();
 }
  
}

And lets use the above DTO and Lambda expressions now.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author twreddy
 *  Streams : Lambda expressions enable you to do this,
 *  to treat functionality as method argument, or code as data.
 *  
 */
public class ArticleTest {

 private List<Article> articles = new ArrayList<Article>();
 
  public static void main(String[] args) {
  ArticleTest at = new ArticleTest();
   at.loadData();
  System.out.println("getAllJavaArticle = "+at.getAllJavaArticle());
  System.out.println("getFirstJavaArticle="+at.getFirstJavaArticle());
  System.out.println("groupByAuthor="+ at.groupByAuthor());
  System.out.println("getDistinctTags="+ at.getDistinctTags());
 }
 
 
 /**
  * LIKE ORACLE LIKE function.
  * @return
  */
 public Optional<Article> getFirstJavaArticle() {  
     return articles.stream()
         .filter(article -> article.getTags().contains("Java"))
         .findFirst();
   }

 
 public List<Article> getAllJavaArticle() {  
      return articles.stream()
         .filter(article -> article.getTags().contains("Java")).collect(Collectors.toList());
   }

 /* OLD way  JDK 7 */
 public Map<String, List<Article>> groupByAuthorJava7() {

     Map<String, List<Article>> result = new HashMap<>();

     for (Article article : articles) {
         if (result.containsKey(article.getAuthor())) {
             result.get(article.getAuthor()).add(article);
         } else {
             ArrayList<Article> articles = new ArrayList<>();
             articles.add(article);
             result.put(article.getAuthor(), articles);
         }
     }

     return result;
 }
 
 /*
  * GROUP BY like in oracle.
  */
 public Map<String, List<Article>> groupByAuthor() {  
     return articles.stream()
         .collect(Collectors.groupingBy(Article::getAuthor));
 }    
 
 /*
  * Like to Extract one property so use flatmap.
  */
 public Set<String> getDistinctTags() {  
     return articles.stream()
         .flatMap(article -> article.getTags().stream())
         .collect(Collectors.toSet());
 }
 
 
 public List<Article> loadData(){
  List<String> l1 = new ArrayList<String>();
  l1.add("Java");
  Article a1 = new Article("Complete reference","Herbert Schildt",l1);
  
  List<String> l2 = new ArrayList<String>();
  l2.add("Algorithms");
  Article a2 = new Article("Datastructures and Algorithms","Padma Reddy",l2);
  
  List<String> l3 = new ArrayList<String>();
  l3.add("Algorithms");
  Article a3 = new Article("Finite automation","Padma Reddy",l3);
  articles.add(a1);
  articles.add(a2);
  articles.add(a3);
  return articles;
  
 }
 
}

More usage of collections.stream()
=============================

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.rama.jdk8.predicate.Employee;

public class BaseStreamTest {
 
 public static void main(String[] args) {
  
  
  List<String> list = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl",null);
  
  //As good as writing a predicate which checks that Stirng is not null.
  List<String> filtered = list.stream().filter(string -> (string !=null) && !string.isEmpty()).collect(Collectors.toList());
  System.out.println("filtered with out predicate using plain lambda expression ="+ filtered);
  
  List<String> filteredWithPredicate = list.stream().filter(isNotNull()).collect(Collectors.toList());
  System.out.println("filteredWithPredicate="+ filteredWithPredicate);
  
  List<String> filteredWithPredicateNulls = list.stream().filter(isNull()).collect(Collectors.toList());
  System.out.println("filteredWithPredicateNulls="+ filteredWithPredicateNulls);
  
 }
 
  public static Predicate<String> isNotNull() {
   return str -> (str != null) && !str.isEmpty();
  }
  
  public static Predicate<String> isNull(){
   return str -> ((str == null)||(str.isEmpty()));
  }

}

At lastly there some new methods on Map class in JDK 8

//Map Class in JdK has some new methods,.  
Map<Integer, String> map = new HashMap<>();
map.putIfAbsent(1, "Rama");
map.putIfAbsent(1, "Chandra");
map.forEach((id, val) -> System.out.println(val));
Code samples can be downloaded at
Lamda Examples Code Download

No comments:

Post a Comment