Friday, 25 November 2016

Design Patterns : Tutorial 2

Visitor Design Pattern

public interface ItemElement {

       public int accept(ShoppingCartVisitor visitor);
}

public class Book implements ItemElement {

       private int price;
       private String isbnNumber;

       public Book(int cost, String isbn){
              this.price=cost;
              this.isbnNumber=isbn;
       }

       public int getPrice() {
              return price;
       }

       public String getIsbnNumber() {
              return isbnNumber;
       }

       @Override
       public int accept(ShoppingCartVisitor visitor) {
              return visitor.visit(this);
       }
}

public class Fruit implements ItemElement {

       private int pricePerKg;
       private int weight;
       private String name;

       public Fruit(int priceKg, int wt, String nm){
              this.pricePerKg=priceKg;
              this.weight=wt;
              this.name = nm;
       }

       public int getPricePerKg() {
              return pricePerKg;
       }

       public int getWeight() {
              return weight;
       }

       public String getName(){
              return this.name;
       }

       @Override
       public int accept(ShoppingCartVisitor visitor) {
              return visitor.visit(this);
       }
}

public interface ShoppingCartVisitor {

       int visit(Book book);
       int visit(Fruit fruit);
}

public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

       @Override
       public int visit(Book book) {
              int cost=0;
              //apply 5$ discount if book price is greater than 50
              if(book.getPrice() > 50){
                     cost = book.getPrice()-5;
              }else cost = book.getPrice();
              System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
              return cost;
       }

       @Override
       public int visit(Fruit fruit) {
              int cost = fruit.getPricePerKg()*fruit.getWeight();
              System.out.println(fruit.getName() + " cost = "+cost);
              return cost;
       }
}

public class ShoppingCartClient {

       public static void main(String[] args) {
              ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
                           new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};

              int total = calculatePrice(items);
              System.out.println("Total Cost = "+total);
       }

       private static int calculatePrice(ItemElement[] items) {
              ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
              int sum=0;
              for(ItemElement item : items){
                     sum = sum + item.accept(visitor);
              }
              return sum;
       }

}

Output:
Book ISBN::1234 cost =20
Book ISBN::5678 cost =95
Banana cost = 20
Apple cost = 25
Total Cost = 160

Benefits of Visitor pattern:

The benefit of this pattern is that if the logic of operation changes, then we need to make change only in the visitor implementation rather than doing it in all the item classes.

Another benefit is that adding a new item to the system is easy, it will require change only in visitor interface and implementation and existing item classes will not be affected.

Composite Design Pattern

This pattern is used to populate a tree like structure. The real example for this can be shown as an hierarchy in company where there is CEO under him the GM then the Manager and finally the Employee. This consists of three parts.

  • Component
    • declares interface for objects in composition.
    • implements deafault behaviour for the interface common to all classes as appropriate.
    • declares an interface for accessing and managing its child components.
  • Leaf
    • represents leaf objects in the composition.A leaf has no children.
    • defines behaviour for primitive objects in the composition.
  • Composite
    • defines behaviour for components having children.
    • stores child components.
    • implements child related operations in the component interface.
  • Client
    • manipulates objects in the composition through the component interface.

public interface Employee {

     public void add(Employee employee);

     public void remove(Employee employee);

     public Employee getChild(int i);

     public String getName();

     public double getSalary();

     public void print();

}

public class Manager implements Employee{


 private String name;

 private double salary;


 public Manager(String name,double salary){

  this.name = name;

  this.salary = salary;

 }



 List<Employee> employees = new ArrayList<Employee>();

 public void add(Employee employee) {

    employees.add(employee);

 }


 public Employee getChild(int i) {

  return employees.get(i);

 }


 public String getName() {

  return name;

 }


 public double getSalary() {

  return salary;

 }


 public void print() {

  System.out.println("-------------");

  System.out.println("Name ="+getName());

  System.out.println("Salary ="+getSalary());

  System.out.println("-------------");



  Iterator<Employee> employeeIterator = employees.iterator();

    while(employeeIterator.hasNext()){

     Employee employee = employeeIterator.next();

     employee.print();

    }

 }


 public void remove(Employee employee) {

  employees.remove(employee);

 }

}

public class Developer implements Employee{


    private String name;

    private double salary;


    public Developer(String name,double salary){

        this.name = name;

        this.salary = salary;

    }

    public void add(Employee employee) {

        //this is leaf node so this method is not applicable to this class.

    }


    public Employee getChild(int i) {

        //this is leaf node so this method is not applicable to this class.

        return null;

    }


    public String getName() {

        return name;

    }


    public double getSalary() {

        return salary;

    }


    public void print() {

        System.out.println("-------------");

        System.out.println("Name ="+getName());

        System.out.println("Salary ="+getSalary());

        System.out.println("-------------");

    }


    public void remove(Employee employee) {

        //this is leaf node so this method is not applicable to this class.

    }


}

public class CompositeDesignPatternMain {


 public static void main(String[] args) {

  Employee emp1=new Developer("John", 10000);

  Employee emp2=new Developer("David", 15000);

  Employee manager1=new Manager("Daniel",25000);

  manager1.add(emp1);

  manager1.add(emp2);

  Employee emp3=new Developer("Michael", 20000);

  Manager generalManager=new Manager("Mark", 50000);

  generalManager.add(emp3);

  generalManager.add(manager1);

  generalManager.print();

 }

}


Observer Design Pattern

This updates the Observers which are registered on the changes of the values. The best example of this can be the waiting and notifying of threads.
PFB the example.


public interface Observer {

       public void update(float temp,float humidity,float pressure);

}

public interface Subject {

       public void registerObservers(Observer o);
       public void removeObservers(Observer o);
       public void notifyObservers();

}

public interface DisplayElement {

       public void display();

}

public class WeatherData implements Subject {

       private ArrayList<Observer> observers;
       private float temperature;
       private float humidity;
       private float pressure;

       public WeatherData(){
              observers = new ArrayList<Observer>();
       }

       public void notifyObservers(){
              for(Observer observer:observers){
                     observer.update(temperature, humidity, pressure);
              }
       }

       public void measurementsChanged(){
              notifyObservers();
       }

       public void setMeasurements(float temperature,float humidity,float pressure){
              this.temperature = temperature;
              this.humidity = humidity;
              this.pressure = pressure;
              measurementsChanged();
       }

       @Override
       public void registerObservers(Observer o) {
              observers.add(o);

       }

       @Override
       public void removeObservers(Observer o) {
              int i = observers.indexOf(o);
              if(i >= 0){
                     observers.remove(i);
              }
       }

}


public class CurrentConditionsDisplay implements Observer,DisplayElement {

       private float temperature;
       private float humidity;
       private Subject weatherData;

       public CurrentConditionsDisplay(Subject weatherData){
              this.weatherData = weatherData;
              weatherData.registerObservers(this);
       }

       @Override
       public void display() {
              System.out.println("Current Conditions: "+temperature + "F degrees and "
                           + humidity + "% humidity");

       }

       @Override
       public void update(float temp, float humidity, float pressure) {
              this.temperature = temperature;
              this.humidity = humidity;
              display();
       }

}

public class WeatherStation {

       public static void main(String[] args) {
              WeatherData weatherData = new WeatherData();

              CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);

              weatherData.setMeasurements(80, 65, 30.4f);
              weatherData.setMeasurements(82, 70, 29.2f);
              weatherData.setMeasurements(78, 90, 29.2f);

       }

}

The Output of this is:

Current Conditions: 0.0F degrees and 65.0% humidity
Current Conditions: 0.0F degrees and 70.0% humidity
Current Conditions: 0.0F degrees and 90.0% humidity



Design Patters : Tutorial 1

These can be majorly divided into three types:

  1. Creational Pattern : These patten create a way hiding the creation logic.
               1. Singleton
               2. FactoryPattern


  1. Structural Pattern : These patterns are concerned with class and object composition. The Composition of these gives an added functionality.
    1. Decorator                                                     
    2. Adapter
    3. Facade
    4. Composite 
  2. Behavioural Pattern : These patterns are concerned with communication between objects.
    1. Template
    2. Visitor
    3. Observer 
Decorator Design Pattern

Usecase : Suppose Star Bucks has various Beverages and it want to charge various customers with different toppings and for this we can use the decorator pattern.

public  abstract class Beverage {

       String description = "Unknown Beverage";

       public String getDescription() {
              return description;
       }

       public abstract double cost();
}

public class Expresso extends Beverage{

       public Expresso(){
              description = "Expresso";
       }

       @Override
       public double cost() {
              System.out.println("Inside Expresso Cost");
              return 1.99;
       }
}

public abstract class CondimentDecorator extends Beverage{

       public abstract String getDescription();
}

public class Mocha extends CondimentDecorator{

       Beverage beverage;

       public Mocha(Beverage beverage){
              this.beverage = beverage;
       }

       public String getDescription(){
              System.out.println("Inside Mocha Description");
              return beverage.getDescription() + " , Mocha ";
       }

       public double cost() {
              System.out.println("Inside Mocha Cost");
              return beverage.cost() + .20;
       }
}

public class Soy extends CondimentDecorator{

       Beverage beverage;

       public Soy(Beverage beverage){
              this.beverage = beverage;
       }

       public String getDescription(){
              System.out.println("Inside Soy Description");
              return beverage.getDescription() + " , Soy ";
       }

       public double cost() {
              System.out.println("Inside Soy Cost");
              return beverage.cost() + .40;
       }
}

public class StarbuzzCoffee {

       public static void main(String[] args) {
              Beverage beverage = new Expresso();
              beverage =  new Mocha(beverage);
              beverage =  new Soy(beverage);

              System.out.println("Description : "+beverage.getDescription()
              +" Cost : "+ beverage.cost());
       }

}

Output of this is:

Inside Soy Description
Inside Mocha Description
Inside Soy Cost
Inside Mocha Cost
Inside Expresso Cost
Description : Expresso , Mocha  , Soy  Cost : 2.59

In this we are adding Mocha and Soy to the Expresso Beverage and hence we override these methods and in the main we call the objects as shown.

RealWorldExample: Java IO Classes.


Facade Design Pattern

The Facade design pattern hides the internal implementation and provides an interface to the user to execute the commands. The example to link to a real world is suppose you want to Watch a movie in your home theatre. Before doing that you need to turn on the popcorn popper,dim the lights, put the screen down etc. For you to remember all these tasks is a challenge and in order to solve this, the Facade design pattern gives the interface which internally handles all these tasks. So lets take an code example of this.

public interface Shape {
       void draw();
}

public class Circle implements Shape {

       public void draw(){
              System.out.println("Rectangle : draw");
       }
}

public class Rectangle implements Shape {

       public void draw(){
              System.out.println("Rectangle : draw");
       }
}

public class Square implements Shape {

       public void draw(){
              System.out.println("Square : draw");
       }
}

public class ShapeMaker {

       private Shape circle;
       private Shape rectangle;
       private Shape square;

       public ShapeMaker(){
              this.circle = new Circle();
              this.rectangle = new Rectangle();
              this.square = new Square();
       }

       public void drawCircle(){
              circle.draw();
       }

       public void drawRectangle(){
              rectangle.draw();
       }

       public void drawSquare(){
              square.draw();
       }
}

public class FacadePatternDemo {

       public static void main(String[] args) {
              ShapeMaker shapeMaker = new ShapeMaker();
              shapeMaker.drawCircle();
              shapeMaker.drawRectangle();
              shapeMaker.drawSquare();
       }
}

Adapter Design Pattern

The best example of a adapter design pattern is that an adapter which is used between the port and the plug.

public interface Duck {

       void quack();
       void fly();
}

public class MallardDuck implements Duck {

       @Override
       public void quack() {
              System.out.println("Quack");

       }

       @Override
       public void fly() {
              System.out.println("I'm flying");

       }
}

public interface Turkey {

       public void gobble();
       public void fly();

}

public class WildTurkey implements Turkey {

       @Override
       public void gobble() {
              System.out.println("Gobble Gobble");
       }

       @Override
       public void fly() {
              System.out.println("I'm flying a short distance");
       }
}

public class TurkeyAdapter implements Duck {

       Turkey turkey;

       public TurkeyAdapter(Turkey turkey){
              this.turkey = turkey;
       }

       @Override
       public void quack() {
              turkey.gobble();
       }

       @Override
       public void fly() {
              for(int i = 0 ; i < 5;i++){
                     turkey.fly();
              }
       }

}

public class DuckTestDrive {

       public static void main(String[] args) {
              MallardDuck duck = new MallardDuck();
              WildTurkey turkey = new WildTurkey();

              Duck turkeyAdapter = new TurkeyAdapter(turkey);
              System.out.println("The Turkey says...");

              turkey.gobble();
              turkey.fly();

              System.out.println("The Duck Says");
              duck.quack();
              duck.fly();

              System.out.println("The TurkeyAdapter says");
              turkeyAdapter.quack();
              turkeyAdapter.fly();
       }
}

Output is:

The Turkey says...
Gobble Gobble
I'm flying a short distance
The Duck Says
Quack
I'm flying
The TurkeyAdapter says
Gobble Gobble
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance
I'm flying a short distance

Template Design Pattern

This designs the skeleton of the algorithms.Some of the methods are implemented by the class and the rest are overridden by the subclass.

public abstract class CaffeineBeverage {

       final void prepareReceipe() {
              boilWater();
              brew();
              pourInCup();
              addCondiments();
       }

       abstract void brew();
       abstract void addCondiments();

       void boilWater(){
              System.out.println("Boiling Water");
       }

       void pourInCup(){
              System.out.println("Pouring into cup");
       }
}

public class Coffee extends CaffeineBeverage {

       @Override
       void brew() {
              System.out.println("Dripping Coffee through Filter");
       }

       @Override
       void addCondiments() {
              System.out.println("Adding Sugar and Milk");
       }
}

public class Tea extends CaffeineBeverage{

       public void brew(){
              System.out.println("Steeping the tea");
       }

       public void addCondiments(){
              System.out.println("Adding Lemon");
       }

}

Saturday, 19 November 2016

Java 8 : Default methods, Optional and Logging

Default methods

Shortcomings of Interface:

The biggest disadvantage of using an interface is suppose you want add an new functionality to it, then all the classes that implement this interface needs to update the new functionality and  hence to avoid this Java8 has allowed to add default methods to the interfaces. The default method looks like below.

default void sort(Comparator<? super E> c){
          Collections.sort(this,c);
}

Difference between Abstract Class and Java 8 interface:

  1. A Class can extend only one abstract class but whereas it can implement multiple interfaces.
  2. An abstract class can interface a common state throughout i.e it has the instance variables but whereas the interface does not.

Note : The default methods are like the abstract methods defined in the interface but with the implementation and these are not static.

As part of Java 8 we can now have static and default methods in the in interface. PFB the example.

public interface A {

       static void helloStatic(){
              System.out.println("Hello from Static A");
       }

       default void hello(){
              System.out.println("Hello from A");
       }
}

Important Points:

  1. All the methods defined in interface are public i.e the default and static methods both are public.
  2. The default is not a access modifier, but its a keyword which is used on a method in a interface which says that its the implemented method in the interface.Note: Even out of the 4 access modifiers public,default,protected and private we never write a method with the default access modifier, but we do not specify any access modifier and it means the default one.

Lets discuss on the different cases on multiple inheritance.

Case 1:

public interface A {
       default void hello(){
              System.out.println("Hello from A");
       }
}

public interface B extends A{
       default void hello(){
              System.out.println("Hello from B");
       }
}

public class C implements B , A {

       public static void main(String[] args) {
              new C().hello();
       }
}

O/P is :Hello from B

Case 2:

public class D implements A {
}

O/P is :Hello from B

Case 3:

public class D implements A {
       public void hello(){
              System.out.println("Hello from D");
       }
}

O/P is :Hello from D

Case 4:

public class C extends D implements B , A {

       public void hello(){
              System.out.println("Hello from C");
       }

       public static void main(String[] args) {
              new C().hello();
       }
}

O/P is :Hello from C

Case 5:

public interface A {
       default void hello(){
              System.out.println("Hello from A");
       }
}

public interface B {
       default void hello(){
              System.out.println("Hello from B");
       }
}

public class C implements B , A {

       public static void main(String[] args) {
              new C().hello();
       }
}

This would through an compile time error because at complie time it does not know which method it should override and hence we would need to add a overridden method is C.

public class C implements A , B {

       public void hello(){
              System.out.println("Hello from C");
       }

       public static void main(String[] args) {
              new C().hello();
       }
}

On adding this it would work fine.

Case 6:

public interface A {

      default void hello(){
              System.out.println("Hello from A");
       }
}

public interface B extends A{
       void hello();
}
public class implements A , B {

       public static void main(String[] args) {
              new C().hello();
       }
}

O/P is: This would throw an compilation error to implement the hello method is C. Always remember that whenever is define an abstract method in interface it needs to be implemented in its child classes.

Case 7:
public interface A {
       void hello();
}

public interface B extends A{
      default void hello(){
              System.out.println("Hello from A");
       }
}
public class implements A , B {

       public static void main(String[] args) {
              new C().hello();
       }
}

O/P is: This would compile and execute fine.

Based on the above cases, the working of the overridden method can be taught as below:

1.Methods defined in the class or one defined in the inherited class or the sub class gets the preferance first. Ex: Case 3
2.The sub-interfaces win and if they are methods with the same signature then the most default one is selected. Ex: Case 1
3.Finally if the choice is still ambiguous then the class needs to explicitly select the default method by overriding it and calling the desired method.

Optional:

One of the challenges with the code is to check a variable if it is null everytime we want to perform any operation on it in order to avoid the NullPointerException. To counter this Java 8 has introduced the Optional.

Creating an Optional Objects:

1.Empty Optional: You can get an empty Optional object using the below code.

Optional<Car> optCar = Optional.empty();

2.Optional from a non null value:

Optional<Car> optCar = Optional.of(car);

If the car is null, a nullpointer error is thrown instead of throwing at the end and failing.

3.Optional from null

Optional<Car> optCar = Optional.ofNullable(car);

If the car is null then the resulting Optional object would be empty.

4.Extracting values using a map:

public class Person {
       private Optional<Car> car;

       public Optional<Car> getCar() {
              return car;
       }
}

public class Car {

       private Optional<Insurance> insurance;

       public Optional<Insurance> getInsurance() {
              return insurance;
       }
}

public class Insurance {

       private String name;

       public String getName() {
              return name;
       }
}

Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

This is similar to the Stream API but instead of many values, here it has a single element.

Chaining Optional Objects with flatMap:

We can get the values using the flatMap.The flatMap works same as it worked in stream.

       public static void main(String[] args) {
              Person person = new Person();
              Car car = new Car();
              Insurance insurance = new Insurance();
              insurance.setName("PQR");
              Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
              car.setInsurance(optInsurance);
              Optional<Car> optCar = Optional.ofNullable(car);
              person.setCar(optCar);
              Optional<Person> optPerson = Optional.ofNullable(person);
              System.out.println(optPerson.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName().get());
       }
We can also provide a default value using the orElse.

System.out.println(optPerson.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

Serialization using Optional:

It does not support serialization and in order to use it for serialization we can use it as below.

public class Person {
       private Car car;

       public Optional<Car> getCar() {
              return Optional.ofNullable(car);
       }
}

Filtering using Optional:

Similar to stream we can filter the values from the Optional.

System.out.println(optInsurance.filter(insurance1 -> "PQR".equals(insurance1.getName()))

Summary : Important methods in Optional.

  1. empty : This returns an empty Optional instance.
  2. filter : If the value is present returns an Optional or else returns an empty.
  3. flatMap : This would flat the structure Optional<Optional<String>> to Optional<String>.
  4. get :  Returns the value in Optional instance or else gives a NosuchElementException and hence this should not be used v ery oftenly.
  5. ifPresent : if the value is present then invokes the Consumer that is provided to it otherwise does nothing.
  6. isPresent : Returns true if there is a value present else returns false.
  7. map : if the value is present applies the corresponding map function. 
  8. of : Returns an Optional wrapping the value or else throws a NullPointer Exception.
  9. ofNullable : Returns an Optional wrapping the value else empty Optional if the value is null.
  10. orElse : Returns the value if present or the given default values.
  11. orElseGet : This is the lazy counterpart of orElse.
  12. orElseThrow : Its similar to get but in case it allows you to select the type of exception you want to throw.
Logging:

 In Java 8 we can log the processing of the stream using the peek method.

public static void main(String[] args) {

List<Integer> result = Stream.of(2, 3, 4, 5)
.peek(x -> System.out.println("taking from stream: " + x)).map(x -> x + 17)
.peek(x -> System.out.println("after map: " + x)).filter(x -> x % 2 == 0)
.peek(x -> System.out.println("after filter: " + x)).limit(3)
.peek(x -> System.out.println("after limit: " + x)).collect(toList());
}



Sunday, 6 November 2016

Java 8 CompletableFuture

CompletableFuture:

The CompletableFuture provides various features like below which was difficult to implement using the Future API.

  1. Combining two asynchronous computations in one.
  2. Waiting for the completion of all the tasks performed by a set of Futures.
  3. Waiting for the completion of the quickest task.
  4. Reacting to a future completion i.e to perform a further action using the result instead of being blocked waiting for the result.

Below is the example of CompletableFuture used in code:

       public Future<Double> getPriceAsync(String product){
              CompletableFuture<Double> futurePrice = new CompletableFuture<>();
              new Thread(() -> {
                     try{
                           double price = calculatePrice(product);
                           futurePrice.complete(price);
                     }catch(Exception ex){
                           futurePrice.completeExceptionally(ex);
                     }
              }).start();
              return futurePrice;
     }

The equivalent code for this is:

       public Future<Double> getPriceAsync(String product){
              return CompletableFuture.supplyAsync(() -> calculatePrice(product));
       }

The supplyAsync returns a CompletableFuture<U> where U is the type which we have supplied.Also internally it makes use of the executor using which the supplier executes.

Make your code non blocking:

Sequential Mode:  Here due to blocking this gives a poorest performance as we are not using all the resources.

       public static List<String> findPricesSequentially(String product){
              return shops.stream().map(shop -> String.format("%s price is %.2f",
                            shop.getName(),shop.getPrice(product))).collect(Collectors.toList());
       }

Parallel Mode: This mode is faster then the sequential mode and it utilizes all the required resources. Suppose I have 4 core available in my system to execute the task then this would use a threadpool of size 4. The number of procesors is got by using the below command.

Runtime.getRuntime().availableProcessors();

       public static List<String> findPricesUsingParallel(String product){
              return shops.parallelStream().map(shop -> String.format("%s price is %.2f",
                            shop.getName(),shop.getPrice(product))).collect(Collectors.toList());
       }

ComplitableFuture Mode: Below is the code for this:

       public static List<Double> findPricesUsingFuture(String product){
               List<CompletableFuture<Double>> priceFutures = shops.stream().map(shop -> CompletableFuture.supplyAsync(() ->shop.getPrice(product))).collect(Collectors.toList());
               return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
       }

This mode performance is faster then sequential mode but it is slower then the parallel mode.

Performance of Various Computation:

In this we are going to analyse the performance of different computations. PFB the code.

public class Shop1 {
       private String name;

       public String getName() {
              return name;
       }

       public void setName(String name) {
              this.name = name;
       }

       public Shop1(String name){
              this.name = name;
       }
}

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;

public class ComparisionTest {

       static Random random = new Random();

       static List<Shop1> shops = Arrays.asList(new Shop1("BestPrice"),
                     new Shop1("LetsSaveBig"),
                     new Shop1("MyFavouriteShop"),
                     new Shop1("BuyItAll"));

       public static double getPrice(String product){
              return calculatePrice(product);
       }

       private static double calculatePrice(String product){
              delay();
              return random.nextDouble() * product.charAt(0) + product.charAt(1);
       }

       private static final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100),new ThreadFactory(){
              public Thread newThread(Runnable r){
                     Thread t = new Thread(r);
                     t.setDaemon(true);
                     return t;
              }
       });

       public static void delay(){
              try{
                     Thread.sleep(1000L);
              } catch (InterruptedException ex){
                     throw new RuntimeException(ex);
              }
       }

       public static List<String> findPrices(String product){
              return shops.stream().map(shop -> String.format("%s price is %.2f",
                            shop.getName(),getPrice(product))).collect(Collectors.toList());
       }

       public static List<String> findPricesUsingParallel(String product){
              return shops.parallelStream().map(shop -> String.format("%s price is %.2f",
                            shop.getName(),getPrice(product))).collect(Collectors.toList());
       }

       public static List<Double> findPricesUsingFuture(String product){
               List<CompletableFuture<Double>> priceFutures = shops.stream().map(shop -> CompletableFuture.supplyAsync(() ->getPrice(product))).collect(Collectors.toList());
               return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
       }

       public static List<Double> findPricesUsingFutureExecutor(String product){
               List<CompletableFuture<Double>> priceFutures = shops.stream().map(shop -> CompletableFuture.supplyAsync(() ->getPrice(product),executor)).collect(Collectors.toList());
               return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
       }

       public static void main(String[] args) throws Exception {
              long start = System.nanoTime();
              findPrices("myPhone27S");
              long duration = (System.nanoTime() - start) / 1_000_000;
              System.out.println("Sequential Stream in "+ duration + " msecs");

              start = System.nanoTime();
              findPricesUsingParallel("myPhone27S");
              duration = (System.nanoTime() - start) / 1_000_000;
              System.out.println("Parallel Stream in "+ duration + " msecs");

              start = System.nanoTime();
              findPricesUsingFuture("myPhone27S");
              duration = (System.nanoTime() - start) / 1_000_000;
              System.out.println("Completable Future in "+ duration + " msecs");

              start = System.nanoTime();
              findPricesUsingFutureExecutor("myPhone27S");
              duration = (System.nanoTime() - start) / 1_000_000;
              System.out.println("Completable Future Using Executor in "+ duration + " msecs");
       }
}

Case 1: Tasks Submitted : 4 (Number if shops)
Output of this is:

Sequential Stream in 4162 msecs
Parallel Stream in 1005 msecs
Completable Future in 2003 msecs
Completable Future Using Executor in 1014 msecs

Analysing the output:

  1. The Sequential mode is the slowest as it does not use all the available cores and runs sequentially.
  2. The Parallel Stream runs all the 4 parallel tasks parallely using the default thread pool which creates a pool of size 4 which is nothing but the number of processes available.
  3. The CompletableFuture is faster then the Sequential mode but is slower then the CompletableFuture one.
  4. The CompletableFuture with executor is the fastest of these.

Case 2: Tasks Submitted : 5 (Increasing the number of shops)
Output of this is:

Sequential Stream in 5204 msecs
Parallel Stream in 2005 msecs
Completable Future in 2004 msecs
Completable Future Using Executor in 1003 msecs

Analysing the output:

  1. The Sequential mode takes an extra second as it is working in non parallel.
  2. The Paralllel mode also takes an extra second because it uses the thread pool of only 4 threads only which gets it using Runtime.getRuntime().availableProcessors(). So this waits for one of the threads to be completed, so that it submits the next task.
  3. The ComplitableFuture is faster or almost equivalent to the parallel mode. These two uses the same threadpool and hence the performance is similar.
  4. The ComplitableFuture is the fastest of all the above. Here we are providing the customizable executor then using the default one.

Lets analyse why the ComplitableFuture with executor is faster then the others after doing the next test.

Case 3: Tasks Submitted :9
Output of this is:

Sequential Stream in 9244 msecs
Parallel Stream in 3028 msecs
Completable Future in 3019 msecs
Completable Future Using Executor in 1003 msecs

Analysing the output:

Lets analyse the CompletableFuture using customizable executor only because we know the output of the rest based on our previous analysis.
First lets understand the benefits of creating a threadpool with the appropriate size.

Sizing thread pools:

Creating a thread for every task is a costly affair and slows down the performance of the application since we need to allocate the required size. In order to save us from this we create a threadpool in which we can reuse the threads that have completed the tasks and this improves the applications performance. But the issue in this is what would be the ideal size of the threadpool.

From the great book of Java Concurrency in practice has advised us to give the optimal size of the threadpool. This is because if the number of threads in the pool is too big , they 'll end up competing for scarce CPU and memory resources, wasting their time performing context switching. Conversely, if the number is too small, some of the cores of the CPU will be underutilised. The ideal pool size can be determined using the below formula.

N of threads = N of CPU + U of CPU + (1 + W/C)

N of CPU : is the number of cores available through Runtime.getRuntime().availableProcessors().
U of CPU : is the target CPU utilization (between 0 and 1) and
W/C : is the ratio of wait time to compute time. Wait time is the one which is got if the process is waiting for the response from other resources.

In our case below are the values:

N of CPU : 4
U of CPU : 1
W/C : Since we are waiting for the response from the shops then it is 100%.

Hence N of threads = 400.

Below is the executor we used in the code.

       private static final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100),new ThreadFactory(){
              public Thread newThread(Runnable r){
                     Thread t = new Thread(r);
                     t.setDaemon(true);
                     return t;
              }
       });

Coming back to the performance of CompletableFuture using executors, since each shop uses a single thread and there is no much context switching and hence this is the fastest.

Parallelism - via streams Vs ComplitableFuture:

If you are doing computation heavy operation without any IO then the stream interface gives the simpliest implementation and one likely the most efficient  - if all threads are compute bound , then there is no point of having more threds then the processor core.

On the other hand if your parallel units of work involve waiting for I/O then CompletbleFuture gives more flexibility.

Pipelining asynchronous tasks:

Below is the code for this. In this I am comparing the performance between stream, parallel stream and asynchronous pipelining.

package com.futures;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;

import com.futures.ExchangeService.Money;

public class Shop {

       private String name;
       Random random = new Random();

       static List<Shop> shops = Arrays.asList(new Shop("BestPrice"),
                     new Shop("LetsSaveBig"),
                     new Shop("MyFavouriteShop"),
                     new Shop("BuyItAll")
                     );

       private static final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100),new ThreadFactory(){
              public Thread newThread(Runnable r){
                     Thread t = new Thread(r);
                     t.setDaemon(true);
                     return t;
              }
       });


       public String getName() {
              return name;
       }

       public void setName(String name) {
              this.name = name;
       }

       public Shop(String name){
              this.name = name;
       }

       public double getPrice(String product){
              return calculatePrice(product);
       }

       public String getPriceWithDiscount(String product){
              double price = calculatePrice(product);
              Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)];
              return String.format("%s:%.2f:%s", name,price,code);
       }


       public Future<Double> getPriceAsync(String product){
              return CompletableFuture.supplyAsync(() -> calculatePrice(product));

       }

       public static void delay(){
              try{
                     Thread.sleep(1000L);
              } catch (InterruptedException ex){
                     throw new RuntimeException(ex);
              }
       }

       private double calculatePrice(String product){
              delay();
              return random.nextDouble() * product.charAt(0) + product.charAt(1);
       }


       public static  List<String> findPricesUsingDiscountedService(String product){
              return shops.stream()
                           .map(shop -> shop.getPriceWithDiscount(product))
                           .map(Quote::parse)
                           .map(Discount::applyDiscount)
                           .collect(Collectors.toList());
       }

       public static  List<String> findPricesUsingParallelDiscountedService(String product){
              return shops.parallelStream()
                           .map(shop -> shop.getPriceWithDiscount(product))
                           .map(Quote::parse)
                           .map(Discount::applyDiscount)
                           .collect(Collectors.toList());
       }

       public static List<String> findPricesUsingFutureOpr(String product){
              List<CompletableFuture<String>> priceFutures = shops.stream()
                           .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPriceWithDiscount(product),executor))
                           .map(future -> future.thenApply(Quote::parse))
                           .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote),executor)))
                           .collect(Collectors.toList());

              return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
       }



       public static void main(String[] args) throws Exception {

              long start = System.nanoTime();
              findPricesUsingDiscountedService("myPhone27S");
              long duration = (System.nanoTime() - start) / 1_000_000;
              System.out.println("Completable Future Using stream "+ duration + " msecs");

              start = System.nanoTime();
              findPricesUsingParallelDiscountedService("myPhone27S");
              duration = (System.nanoTime() - start) / 1_000_000;
              System.out.println("Completable Future Using parallel stream "+ duration + " msecs");

              start = System.nanoTime();
              findPricesUsingFutureOpr("myPhone27S");
              duration = (System.nanoTime() - start) / 1_000_000;Th
              System.out.println("Completable Future Operation Using asynchronous pipeline "+ duration + " msecs");

       }
}

Output of this is:

Completable Future Using stream 8183 msecs
Completable Future Using parallel stream 2019 msecs
Completable Future Operation Using asynchronous pipeline 2013 msecs

Lets discuss on the various methods of asynchronous pipelining on ComplitableFuture.

  1. thenApply: This method does not block till the CompletableFuture is finally completes. But this tells what to do once we get the required output.
  2. thenCompose: This method is specifically for the purpose of pipelining two asynchronous operations passing the result of the first operation to the second operation. This would use the same thread of the first task and it has a variant called thenComposeAsync and this would submit the task to a threadpool.
  3. thenAccept: This takes an argument as Consumer and returns a CompletableFuture<Void>. This is usually the final step and is used when we have various tasks and want to do a third operation on the completion of the tasks and we want to ensure we finish the tasks as and when they are completed instead of waiting for all the tasks to be completed in a given order.Below is the code.

        CompletableFuture[] futures = findPricesStream(product)
                .map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)")))
                .toArray(size -> new CompletableFuture[size]);
        CompletableFuture.allOf(futures).join();

        Output of this is:

BuyItAll price is 136.72 (done in 1928 msecs)
MyFavouriteShop price is 175.36 (done in 2664 msecs)
BestPrice price is 193.51 (done in 2792 msecs)
LetsSaveBig price is 173.58 (done in 3420 msecs)

        We submit all the tasks to the thenAccept method which ever completes first prints the values as shown in the output. In order to ensure that all the tasks are completed then we save it to an Array and then we call the allOf       method on the CompletableFuture which waits for all the tasks to be completed.
     4.  thenCombine: If i want to do a task on the completion of two different asynchrnous tasks then I make use of this. Below is the code example of this.

    public static List<String> findPricesInUSD(String product) {
        List<CompletableFuture<Double>> priceFutures = new ArrayList<>();
        for (Shop shop : shops) {
            CompletableFuture<Double> futurePriceInUSD =
                CompletableFuture.supplyAsync(() -> shop.getPrice(product))
                .thenCombine(
                    CompletableFuture.supplyAsync(
                        () ->  ExchangeService.getRate(Money.EUR, Money.USD)),
                    (price, rate) -> price * rate
                );
            priceFutures.add(futurePriceInUSD);
        }
        List<String> prices = priceFutures
                .stream()
                .map(CompletableFuture::join)
                .map(price -> " price is " + price)
                .collect(Collectors.toList());
        return prices;
    }

Output of this is:

 price is 163.88456508124327
 price is 91.52218405797247
 price is 128.35890489760422
 price is 118.42066871703744


Here in the first task I am calculating the price and in the second the conversion ratio and finally combining these two to calculate the final price.