Monday, 17 August 2015

Java 8 New Features

Java 8 comes with very exciting features of functional programming with lambda expression and streams.

Lets take a look at it features

Lambda Looping

Lambda expression can be used for looping over a Collection.


List<String> names = new ArrayList<String>();
names.add("John");
names.add("David");
names.add("Anthony");

Prior to Java 8
for(String s:names){
    System.out.println(s);
}

Java 8
names.forEach(x -> 
 System.out.println(x) 
);

x here is the variable for each element in the collection. No need to define it.

Another Flavour
names.forEach(x -> {
 if(x.equals("David")){
  System.out.println("Hi David");
 }else{
  System.out.println(x); 
 }
}); 

Here we wrote multiple lines as a block within curly braces. Variable x is available in this block.


Filtering Stream and Collection

Stream provides features like filter, sort, collect and many more on Java Collection. They provide a cleaner and easy way to fetch exactly what you desire from a collection.

Imagine fetching only 5 distinct records in sorted manner from a collection.
Stream makes you achieve this only in one line.

Lets look at a more simple example.

names.stream()
        .filter(country -> country.contains("a")).sorted()
        .forEach(x -> System.out.println(x));

Here we filter only those Strings which contain String 'a'. So only David will be filtered out.
Then we again iterate the list which now contains only David and print it.

Lets do something more complex. Here is a Employee class

public class Employee implements Comparable<Employee>{

 Integer id;
 String name;
 public int getId() {
  return id;
 }
 public void setId(Integer id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public Employee(Integer id, String name) {
  super();
  this.id = id;
  this.name = name;
 }
 @Override
 public String toString() {
  return "Employee [id=" + id + ", name=" + name + "]";
 }
 @Override
 public int compareTo(Employee o) {
  return this.id-o.id;
 }
}

Lets put the instances of Employee in a collection
List<Employee> employeeList= Arrays.asList(
new Employee(-5, "John"),
new Employee(2, "David"),
new Employee(1, "Anthony")
);  
Lets filter and sort the collection

We want the list of Id's in sorted order of Employees who have Id greater than -100


List<Integer> sortedNames =
 employeeList.stream()
 .filter(employee -> employee.getId()>-100)
 .sorted()
 .map(Employee :: getId)
 .collect(Collectors.toList());

sortedNames.forEach( id -> System.out.println(id));

Lets understand what happened here.

1. filter is used to filter Employee with id greater than -100
2. Then we sorted the filtered list of employees using sorted. Sorting is done with the help of comparable interface already used in Employee class.
3. To collect the filtered sorted Id's we used map. It fetches the Id's from Employee class.
4. And collects into the List, sortedNames.

Stream provide more features such as
1. distinct - Returns a stream with unique elements (according to the implementation of equals for a stream element)
2. limit(n) - Returns a stream that is no longer than the given size n
3. skip(n) - Returns a stream with the first n number of elements discarded
4. max(comparator) - Returns the maximum element of the stream based upon the comparator.
5. min(comparator) - Returns the minimum element of the stream based upon the comparator.

Imagine below statements.

1. Fetch employee having the maximum salary (max)
2. Fetch students from a class having name 'John' (filter or distinct)
3. Fetch employee Id's who have annual rating less than 3 (filter and map)
4. Fetch Tourist place to visit having highest rating where pets are allowed.(max and filter)

Lambda Method Reference

Lambda enables you to write more clearer anonymous classes. But the interface should have only one abstract method. Lets take an example.

Prior to Java 8

new Thread(new Runnable() {
 
 @Override
 public void run() {
  System.out.println("this is a thread");
  
 }
}).start();

You definitely might have written such code many times. This is a anonymous inner class example. With Lambda this becomes more cleaner.
Java 8

new Thread(
 () -> {System.out.println("This is a thread");}
).start();

Since the Runnable interface has only one abstract method with lambda there is no need to define the method. The empty round brackets ( ) define the method run with no parameters. Whatever is written in curly braces { } after -> is the body of the run method.

No lets go back to our example of Employee class and try to use method reference.
List<Integer> sortedWithId =
 employeeList.stream()
 .filter(employee -> employee.getName().contains("a"))
 .sorted((Employee e1, Employee e2) -> e1.id-e2.id)
 .map(Employee :: getId)
 .collect(Collectors.toList());

Look closely at the sorted method parameter. In the previous code, we left the sorted parameters blank. This told the sort to look for the comparable interface in Employee class. To sorted method also takes a comparator interface.

Notice here we are not defining the compare method, just the parameters and the method body comparing the id's. No need to even return anything. Lambda does it for you.

Predicate

Predicate is new feature of Java 8. It is basically a condition which can be reused again.

Lets see an example:
Predicate<Employee> checkIdgreaterThanZero= e ->e.getId()>0;

boolean result=checkIdgreaterThanZero.test(new Employee(20,"tttt"));

System.out.println(result);

Here checkIdgreaterThanZero is the condition that we want to use. The expression written on the right hand side should compute to a boolean.

e ->e.getId()>0

This means employee with id greater than 0

This predicate can be than used to test against a employee reference.
boolean result=checkIdgreaterThanZero.test(new Employee(20,"tttt"));
Above here we are testing the instance of Employee with the predicate. The return is a boolean.

One can also combine 2 predicates,

Predicate checkNameContainAAA= e -> e.getName().contains("a");
Above is the new predicate which checks employee whose name contains letter 'a'
Lets combine the 2 predicates

boolean newResult=checkIdgreaterThanZero.and(checkNameContainAAA).test(new Employee(20,"tttt"));
Here a key word and is used to combine the 2 predicates.

Similarly a keyword or is also available.

1 comment:

  1. Great Stuff!! This helps a lot in understanding easily the new features of Java 1.8.

    ReplyDelete

Share the post