As a software developer, all of us come across SQL queries and databases at least a few times in the development process. All of us have written multiple queries. Starting with the most simple select queries to very complex nested query statements and procedures. Wouldn’t it be exciting to know about how the SQL server goes about executing these queries? Also you might have noticed that sometimes our SQL query takes longer that expected to return the result? Why query is slow? The reason for this delay might be found out in the SQL Query Execution plans.

The SQL server surely must use some strategy in order to execute the queries and fetch data efficiently from the database. This task of interpreting the query and analyzing it is done by a component called Query Optimizer. This query optimizer takes the query, database schema (tables) and indexes as inputs and generates the Execution plan for the query.

What are SQL Query Execution plans?

A simple definition of an execution plan can be given as: The query optimizer's way to determine, efficient execution of the SQL statement presented before it, is known as an SQL query execution plan.

Almost all the doubts regarding the execution of the query, optimal usage of indexes, number of tables scans during a query execution can be solved by taking a look at the execution plan for the query. This blog is an attempt to understand the execution plans and know how they can be useful while writing a good query.

Before we go into the details of an execution plan, let's have a quick peek at how the execution plan looks like. There can off course be slight differences in how different SQL clients present them. But this is just to have an idea of what we intend to study:

sql query execution steps

The SQL statement goes through the following stages during execution:

    1. Parsing
    1. Optimizing
  1. Execution

Let us take a look at these phases in short,

Parsing:

Once the query is submitted to the SQL Server for the first time, it is checked whether the query properly formed. The syntax of the query is correct. This is known as parsing of the query.

Now, what points are considered while checking the syntax of the query? Points such as, resolving names of different tables, columns, objects within the query string, identifying datatypes for the columns and location of different functions like aggregate functions in the query.

Important point to note here is that this parsing is done only for DML queries. DML queries are Data Manipulation Logic queries. These queries are which perform addition, deletion, updation and selection operations on the data in the database. Thus there can be multiple ways to perform these operations effectively. In contrast to this, the DDL queries. These are the Data Definition Logic Queries. These queries concern with the creation deletion and updation of database or table structures. There is only one way to correctly create/update/delete tables or database structures and hence parsing of these queries is not necessary.

This process is known as query parsing, and the output for this process is a parse tree. The parse tree contains the logical steps as to how the query needs to be executed. This parse tree is passed on to the query optimizer.

Optimizing:

As we are speaking of execution plans, this section is quite important for us. The generation and evaluation of an execution plan is done by the optimizer. The optimizer decides how to execute the given SQL statement efficiently. Depending upon application of joins, usage of indexes in the query and the cost to perform these operations (in terms of CPU processing and I/O cycles)the optimizer selects an efficient execution plan. Hence, it is also known as a cost-based plan.

The optimizer generates many execution plans and evaluates them against each other to select the most efficient one. It decides the efficiency of the plan depending upon minimum resource utilization(CPU and I/O) and how quickly it returns the result for the query.

The optimizer uses a CPU intensive program to evaluate these execution plans. Therefore if the SQL statement presented to the optimizer is simple then the optimizer generates a single trivial-plan. This execution plan might not be very quick to return results, but as compared to the resource usage for the evaluation of different plans for the query, this plan is then beneficial.

How does it optimizes?

Now, one might think, optimizer is just a program, how is it able to decide efficiency of the execution plan? For this purpose, there is a lot of data about the different indexes, columns and their frequency of occurrence in a SQL statement present in the SQL server. These are known as statistics. Every time a query is executed the data about the Where clause, the Joins, the Indexes is added to these statistics.

Depending upon the query tree received from the parser and these statistics the optimizer determines the execution time required for every step of the query tree. It can then evaluate the different possibilities of execution (execution plans). These possibilities can be by rearranging joins, testing different varieties of joins and indexes etc. to find out the efficient execution plan.

Each logical operation in the query tree is assigned an estimated amount of time. The sum total of the execution time of all the operations in a query then becomes the estimated cost of execution for the query.

An important thing to note here is, all the execution time calculated here is an estimate of the time that might be needed to perform an operation. Given an infinite amount of time and complete, up-to-date statistics, the optimizer would find the perfect plan for executing the query.

These generated plans are then stored in the memory of the server in the plan-cache. Before the plan is actually stored it is checked whether the identical plan already exists in the cache[read more in Execution plan reuse]. If an identical plan is found in the cache, that plan is used for execution and the newly generated plan is not stored.

sql query execution plan creation flow

Execution:

Once the execution plan is generated, the query is actually executed, according to the plan. We shall learn about the details of execution later as it doesn’t concern our execution plans, except to note that the carefully generated execution plan may change during the actual execution process.

Why would this happen?

    • The statistics used to generate the plan were old and out of date or they have changed from the time the optimizer generated the execution plan. Or
  • Execution engine comes across that the query crosses the threshold for parallel execution.

The results of the query are returned depending upon the successful execution of the actual execution plan.

So far in the discussion we came across two terms: Estimated execution plan and Actual execution plan. Let us dig a bit deeper into why these two plans are required. Firstly, these can be defined as below:

Estimated query execution plans: Estimated plans provide an estimation of the work that SQL server is expected to perform to get the result.

Actual query execution plans: Actual execution plans are generated after the SQL queries are executed. Thus, an actual execution plan contains run-time information, such as the actual resource usage and run-time warnings if any.

Estimated and Actual SQL query execution plans

In the general scenario, estimated and actual execution plans do not tend to differ. But sometimes it may happen that the estimated and actual execution plans are different. Let's see some factors that may play a role in the generation of these differences in the execution plans.

Outdated statistics:

As discussed in the Optimizer section above, the SQL server maintains statistics(metadata) for the SQL queries being executed. Also, these statistics are subject to change on every query execution. For example, when new data is added, or existing data is deleted or updated the values that represent the indexes in the metadata might change. Over a longer period of time the statistics may no longer represent the actual data appropriately.

Furthermore, if the server picks up the existing execution plan to work with, then the changed metadata might affect the query execution. Hence to optimize the solution, the actual execution plan comes into play. This plan modifies the existing execution in order to fit to the changed statistics and structures.

Not having run time data:

The execution plans are made before the actual queries are executed. Thus, there are some points where we create temporary tables or views in our query. But these are only created during the execution of the query. Hence, the estimated plans do not take them into account. Consequently generating a faulty estimation plans.

In such cases the actual execution plans are more helpful instead.

Reuse of an execution plan

As said in the discussion above, the optimizer stores the plan into the plan-cache after generation. But if there is already an actual plan present in the cache identical to the current one, then the current plan is not stored. So said, each plan is stored once in the memory of the server.

If parallelism is supported for a query, then there is a possibility to have more than one execution plans for that query. But these plans contain different set of operations that can be executed parallelly.

Thus we can say the execution plans are available for reuse. But there is a specific period of time after which the execution plans are removed from the memory. Each time a plan is stored it is associated with a concept of "Age". This is calculated depending upon the execution cost of the plan and the number of times it is used.

Age = (estimated execution cost of plan) * (No. of times plan used)

The estimated plan is removed from the memory when:

  • There is more memory requirement
  • The Age factor has reached 0

The memory management of the server scans all the caches including the plan-cache at regular intervals to perform the cleanup.

Fast track reading:

  • The query optimizer's way to determine, efficient execution of the SQL statement presented before it, is known as an execution plan
  • The SQL statement goes through the following stages during execution: Parsing, Optimizing, Execution
  • Checking whether the query properly formed is known as parsing of the query
  • Generation and evaluation of an execution plan is done by the query optimizer
  • Efficiency of the plan is decided depending upon minimum resource utilization(CPU and I/O) and how quickly it returns the result for the query
  • These generated plans are then stored in the memory of the server in the plan-cache
  • Estimated plans provide an estimation of the work that SQL server is expected to perform to get the result.
  • Actual execution plans are generated after the SQL queries are executed. Thus, an actual execution plan contains run-time information, such as the actual resource usage and runtime warnings if any.

References:

https://www.red-gate.com/simple-talk/sql/performance/execution-plan-basics/

https://developer.rackspace.com/blog/understanding-a-sql-server-execution-plan/

https://docs.microsoft.com/en-us/sql/relational-databases/performance/execution-plans?view=sql-server-2017

This blog explains the Java 8 Stream API and most common usage. After reading this blog you should be able to work with stream API. Java stream produces pipe-lined data to perform different operations on group of elements. Streams internally iterate over the data and perform operations, then returns the result of functions performed if any.

Java.util.stream package was added in java 8. Java Stream API package contains classes that helps to perform various operations on streams or any collection of objects.

It may seem that Streams are similar to Collection. But there is a huge difference in both the programming paradigms. Streams API is not data structure. It iterates over the data that is used to perform different operations. Whereas Collection is in memory data structure which requires all data to be loaded in memory. Stream does not store any of the data in the memory of the computer.

Another noteworthy point is that,  java.util.stream is a package and java.util.stream.Stream is an interface.

Java 8 Stream API workflow

Java stream involves creation of stream, performing intermediate operations followed by terminal operations on the stream. This process or this flow is known as the Stream pipeline. A general high level stream work flow looks as below:

java 8 stream work flow diagram

As we can see, in the first step a stream is created with the given input. This input can be any type of data ranging from arrays, collections also primitives. Then this stream is passed on to the intermediate operations. These in turn produce the output streams and give them to the terminal operations. As a result of which output is generated form the terminal operations.

As we can see stream has 3 steps
1) Creation of java stream object
2) Intermediate operations on java stream  
3) Terminal operation on java stream

Step 1: Creation of Java Stream

There are many different ways to create streams like using methods of Stream class in stream APIs, methods using collections and arrays, Special streams (Stream of primitives), Stream of Strings, Files as a stream etc.

Let us focus on methods using collections and arrays as these are the most commonly used ways to create stream object.
These methods take collections and arrays as input to produce streams out of them.

Create java stream from collections

While creating streams from collection object we can use .stream() method on the collection object like lists, sets etc.

//Creation of simple stream from list
List<String> names = new Arraylist():
names.add(“Alpha”);
names.add(“Beta”);
Stream stream = names.stream();
//Creation of simple stream from Set
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
Stream stream = set.stream();

Create stream from Array using of() method

While creating streams from an array we need to use use .of() method of Stream.java class

//Creation of simple stream from Array
String[] arrays ={ "Alpha", "Beta", "Theta", "Gamma"};
Stream stream = Stream.of(arrays );

Step 2: Intermediate operations on stream in java [ps2id id="intermediateOperations" target=''/]

The operations which returns source object (stream) after processing are known as intermediate operations.(Also known as Non-Terminal operations). As they return stream object we can again call another intermediate or terminal operation on them. This helps to create chained operations one after the other consequently [Chain of Responsibility Design Pattern in Java] , to perform multiple operations on same element. In chained operation the output of one operation becomes the input of next.

Most important point to note stream operations does not change source objects, it returns new stream with updated elements.

The most common operations on java 8 steam object

  • Filter : used for conditional filtering the collection objects
  • Map : convert object from one form to another
  • Flatmap : Used for mapping nested objects in java stream
  • Sorted : for sorting stream elements

Java 8 stream conditional filter

Filter() is most frequent operation required on java streams
This method is used to filter elements as per the required condition . Predicate function is used (java.util.function.Predicate) to define filter criteria. Predicate function can be implemented using lambda expression or method references

//Using Lambda expression [read more]
List<User> users = ... // some list of users data
List<User> usersAbove18Age = users.stream().filter(u -> d.age() > 18).collect(Collectors.toList());

In the above example we are using java stream conditional filter to get user having age greater than 18

Java 8 stream Map

Java stream Map function is used to map object from one form to other of same or different type. It needs Function (java.util.function.Function) which takes one input and gives output of same or another type.
We can use lambda function or method reference to map object

//Using Lambda expression [read more]
List<User> users = ... // some list of users data
List<String> userNamess = users.stream().map(o -> o.getName());
//Using method reference [read more]
List<User> users = ... // some list of users data
List <String>userNamess = users.stream().map(User::getName);

Note: -

Map always transforms one object to exact one object, if we need to map conditional where we may get zero or more object we need to use flat map

Flatmap : map conditional or nested object

Similar to map function, flatmap is also used to convert object with extra feature of flattening the nested object. It means we can use flatmap() for mapping nested objects in stream.
We have seen that map is used to convert or transform object from one to exact one other object.

If we want to transform from one to many or no object, we cannot use map and we need to use flat map. Also if we need to transform nested object, we need to use flat map.

Flatmap is very useful if we need to map objects conditionally without using filter function.

Flat map transfers each element into streams

List<Student> computerDepartment = new ArrayList<>();
computerDepartment.add(new Student(1, “Example1”));
computerDepartment.add(new Student(2, “Example1”));

List <Student> mechanicalDepartment = new ArrayList<>();
mechanicalDepartment.add(new Student(101, “tutorial1”));
mechanicalDepartment.add(new Student(102, “tutorial2”));

List<List<Student> > allStudents = Arrays.asList(computerDepartment, mechanicalDepartment);
allStudents.stream().flatMap(l -> (l.stream()).forEach(System.out::println);

//Conditional mapping example without nested list
computerDepartment.stream().flatMap(s-> (s.getId()>1) ? Stream.of(s):Stream.empty()).forEach(System.out::println);

Sorted : sorting stream elements

Stream elements can be sorted by 3 ways

  1. Natural ordering :- stream.sorted() The objects must implement comparable interface for sorting
  2. Using Comparator:  stream.sorted(comparator), we can create our own comparator or we can use static method provided in Comparator interface, like comparing, comparingInt, etc
    1. list.stream().sorted( (o1, o2)->o1.getAge()-o2.getAge() ): Sorting by age using Lambda to implement comparator
    2. list.stream().sorted(Comparator.comparing(user::getAge)) : sorting by age using comparing method from Comparator
  3. Reverse ordering :- For reverse ordering we can use reverseOrder() method of Comparator.
    1. list.stream().sorted(Comparator.reverseOrder()) : natural order reverse
    2. list.stream().sorted(Comparator.comparing(user::getAge).reversed()) : reverse order by age

Step 3: Terminal operation on stream[ps2id id='streamTerminalOperation' target=''/]

In contrast to the intermediate operations described above, the terminal operations do not return stream as output.
Terminal operation can produce result as primitive or collection or any Object or may not produce any output. The intermediate operations always precede a terminal operation.
Although there can be multiple intermediate operations, but they must be followed by one and only one terminal operation.

ForEach

This method helps in iterating over all elements of a stream and perform some operation on each item.
Takes Consumer function as input which contains the logic we need to implement. We can implement the consumer function using lambda function or method reference

nameList.stream().forEach(System.out::println);
//Print names with length is greater than 5
nameList.stream().filter(n -> n.length() > 5).forEach(System.out::println);

Collect

One  of the most commonly used terminal operation. It allows to convert stream elements to other containers, like list, set etc

List<User> users = ... // some list of users data
//Collecting data as a list
List<User> userAbove18Age = users.stream().filter(o -> d.age() > 18).collect(Collectors.toList());
//Collection data as a set
Set<User> userAbove18Age = users.stream().filter(o -> d.age() > 18).collect(Collectors.toSet());

ToArray

Similar to the collect() method described above, this terminal operation collects the stream data into containers of arrays. In the example below we can see, the stream data can be collected into a generic array of objects, or to a array of a specific type eg. Student [].

List<Student> students = ... // some list of students data
Object[] objects = students.stream().toArray();
Student[] studentArray= students.stream().toArray(Student[]::new);

Note:-

If we do not specify the class type [eg:  toArray(Student[]::new) ], Array will be created for Object class

Min and Max

Finds the minimum or maximum of the list value form list. We need to provide comparator for comparing the elements.
We can create our own comparator or we can use static methods provided in Comparator interface, like comparing(), comparingInt(),etc

List<Student > students = ... //List of students data
//Return student with highest marks
Student topper = students.stream().max(Comparator.comparing(student::getMarks));
//Return student with shortest height
Student shortStudent = students.stream().min(Comparator.comparing(user::getHeight));

Count

Returns the count of elements in stream as a long value

List<User> userList = ... //List of users data
//Count of all users
Long allUserCount = userList.stream().count();
//Count of users above the age of 18
Long userAbove18= userList.stream().filter(u -> u.getAge() > 18).count();

Reduce

Used to reduce elements to single result by combining or processing all the elements.
Returns optional object means null or something
There are three different ways for reduce operation

  • reduce(BinaryOperator accumulator) : Takes the binary Accumulator function to compute the result object and return the Optional instance

    Integer[] noNumbers = {};
    Optional<Integer> reduce1 = Arrays.stream(noNumbers).reduce((a, b) -> a + b);
    System.out.println(reduce1); // OUTPUT = Optional.empty

    Integer[] numbers = {1,2,3};
    Optional<Integer> reduce2 = Arrays.stream(numbers).reduce((a, b) -> a + b);
    System.out.println(reduce2); // OUTPUT = Optional[6]

  • reduce(T identity, BinaryOperator accumulator) : We need two parameters, Identity which works as Return type and initial and Accumulator function.
    Computation logic includes the initial value while computing.

     

    Integer[] noNumbers = {};
    Integer reduce1 = Arrays.stream(noNumbers).reduce(0, (a, b) -> a + b);
    System.out.println(reduce1); // OUTPUT = 0

    Integer[] numbers = {1,2,3};
    Integer reduce2 = Arrays.stream(numbers).reduce(2, (a, b) -> a + b);
    System.out.println(reduce2); // OUTPUT = 8

  • reduce(U identity, BiFunction accumulator, BinaryOperator combiner); Mainly used for parallel streams, Takes 3 arguments Identity which works as Return type and initial, Accumulator function and Combiner for combining the results of different streams in parallel processing.
    Computation logic includes the initial value while computing.

     

    Integer[] numbers = {1,2,3};
    Integer reduce1 = Arrays.stream(numbers).reduce(0,(a, b) -> a + b,(a, b) -> a + b);
    System.out.println(reduce1); // OUTPUT = 6

    List list= Arrays.asList(numbers);
    Integer reduce2 = list.parallelStream().reduce(0,(a, b) -> a + b,(a, b) -> a + b);
    System.out.println(reduce2); // OUTPUT = 6

Match

This method checks if the returned result matches the criteria. It returns boolean value (True/False) depending upon the result.
There are 3 matching methods that are used to match the output with a certain criteria.

  • anyMatch : Check if at least one record is found
  • allMatch : Check if all records are found
  • noneMatch : Checks if no record found
//Ways to match string starting with "M"
boolean result = userList.stream().anyMatch((u) -> s.startsWith("M"));
boolean result = userList.stream().allMatch ((u) -> s.startsWith("M"));
boolean result = userList.stream().noneMatch ((u) -> s.startsWith("M"));

anyMatch() is the short circuit operation as it will return true once it finds the first true condition and stops further processing.

findFirst

Returns the first result from stream in optional(null or something) type

Optional<user> firstRecord = userList.stream().findFirst()
Or
Optional<user> firstRecord = userList.stream().filter(u -> u.length() > 5).findFirst();

findFirst() is the short circuit operation as it returns the first element and stops further processing.

Fast track reading

  • Java stream produces pipe-lined data to perform different operations on group of elements
  • java.util.stream is package and java.util.stream.Stream is an interface
  • Stream is not a datatype
  • Streams API supports serial as well as parallel operations
  • Stream operations can be classified into three types namely : Creation operation, Intermediate operation, Terminal operation
Creation operationIntermediate operationTerminal operation
These functions create streamsThese are functions operating on streams resulting into output streamThese are functions operating on streams to produce non-stream output
Streams are created from various sources, namely: Collections,Array, Strings, Files also primitive data typesThese take stream element as inputThese take stream element as input
These produce stream as outputThese produce stream as outputThe result of functions performed if any are returned.
There can be only one creation operation in stream pipeline (In the beginning)Any number of intermediate operations chained to each otherThere can be only one terminal operation in stream pipeline (At the end)
eg: empty(), stream(), of, etcex: filter, map, flatmap, sorted, etceg: forEach, collect, toArray, min, max, count, etc

References:

In java we refer classes using variables. We call methods using the reference variable of class. Wouldn't it be great if we can refer the methods also? Thanks to java 8 feature releases, we can do it. Method reference in java 8 allows us to refer methods with class and method name directly.

Before going further, you should know about lambda expressions. Using lambda we can create anonymous functions for class without creating object or implementing. This helps to pass small functions as arguments.

Method reference is shorter way of writing lambdas expressions. Sometimes we just have another method call in a method body in such case method reference is used.

Where we can use java method reference

Using simple Java

public void printString(String str){

System.out.println(str);

}

for(User user : userList){

userService.update(user);

}

similarly using lambda

(s) -> System.out.println(s);
users.stream().forEach( user-> userService.update(user);)

These are the cases when we can use java 8 method reference so that code will be clean and easy to read.

Syntax of java 8 method reference

CLass :: mehtodName

Object ::methodNam

Most noteworthy point to notice is that in method reference syntax we use  " :: "  operator not the  " . "  operator. We don’t need to pass arguments. Arguments are provided by the context variables.

The most important point to note is we can only use method reference for lambda expression having single line of another method call.

Different ways of method references in java

Reference TypeLambda ExpressionMethod Reference
Static Method Reference(args) -> Class.staticMethod(args)
eg: (String s) -> Long.parseLong(s)
Class::staticMethod
eg: Long::parseLong
Object Method Reference(args) -> obj.instanceMethod(args)
eg: (s) -> System.out.println(s)
obj::instanceMethod
eg: System.out::println
Instance Method Reference By Class Type(obj, args) -> obj.instanceMethod(args)
eg: (str) -> str.length()
Class::instanceMethod
eg: String::length
Constructor Reference(args) -> new Class(args)
eg: (size)->new ArrayList(size)
Class::new
eg: ArrayList::new

Static method reference in java

As we know in java we can call static methods using class name without creating object. Similarly, we can refer static methods using class name.

StaticClass :: staticMethod

Example of java static method reference

In the example we have MethodReferenceTest class and static method print. Print method can have any logic. We are just printing message on console.

In main method we have created an Optional variable. Then call print method for the variable using class name.

public class MethodReferenceTest {

public static void print(String msg) {

// ….. some logic

System.out.println("Printing: "+msg)

// ….. some logic

}

public static void main(String[] args) {

Optional<String> msg = Optional.of("StackTraceGuru");

//Using Lambda expression

msg.ifPresent(text->MethodReferenceTest.print(text));

//Using Method reference to print method from MethodReferenceTest class

msg.ifPresent(MethodReferenceTest::print);

}

}


Output :
Printing: StackTraceGuru
Printing: StackTraceGuru

In the example we have called print method using lambda expression and equivalent Static method reference format.

Object method reference in java

In java if we need objects to call methods. However if in lambda expression we just have method call using object, We can refer such methods using class objects.
classObject :: methodName

Example of object method reference in java

We will use same example as static method reference, with the small change. First of all we have removed static from print method so it is a normal method now.

public class MethodReferenceTest {

public static void print(String msg) {

System.out.println("Printing: "+msg)

}

public static void main(String[] args) {

Optional<String> msg = Optional.of("StackTraceGuru");

MethodReferenceTest obj =new MethodReferenceTest();

//Using Lambda expression

msg.ifPresent(text-> obj.print(text));

//Using Method reference to print method from MethodReferenceTest class

msg.ifPresent(obj::print);

}

}


Output :
Printing: StackTraceGuru
Printing: StackTraceGuru

In the example we have called print method using lambda expression and equivalent Object method reference format.

Object method reference By class type in java

Method reference can be used, if we have method calling from object while object is provided by context.

In the other words we can say the lambda expression scenario where we are just calling method and object is a input as from argument. Then we can use Method reference using class type. In this case the class type of object is used to called method.

ClassName :: methodName

Example of method reference by class type in java

In the example we have person class. Person class is having method printDetails. In another class we have main method. We have created an Optional variable of person type. We have to call printDetails method for the object.

class Person{

private String name;

public Person(String name) {

this.name = name;

}

public void printDetials(){

System.out.println("Person name is : "+name);

}

}
public class MethodReferenceTest {

public static void main(String[] args) {

Optional<Person> person = Optional.of(new Person("Mac"));

//Using Lambda expression

person.ifPresent(obj -> obj.printDetials());

//Using Method reference to print method from MethodReferenceTest class

person.ifPresent(Person::printDetials);

}

}


Output :
Person name is : Mac
Person name is : Mac

In the example we have called printDetails method  on the person object. The method is not static. It is not invoked using the class name.

Reference to constructor in java

If we are just creating new object in lambda expression, we can use constructor reference.

ClassName :: new

Example of constructor method reference in java

Similarly as previous example we have person class. Person class need a parameter to create new object. In main method we have Optional variable of String. We have to create person object from the name.

class Person{

private String name;

public Person(String name) {

this.name = name;

}

public void printDetials(){

System.out.println("Person name is : "+name);

}

}

public class MethodReferenceTest {

public static void main(String[] args) {

Optional<String> msg = Optional.of("Mac");

//Using Lambda expression

msg.map (str-> new Person(o)).ifPresent(obj -> obj.printDetials());

//Using Method reference to print method from MethodReferenceTest class

msg.map(Person::new).ifPresent(Person::printDetials);

}

}


Output :
Person name is : Mac
Person name is : Mac

In the example we have created person object using parameterized constructor. After that we have called printDetails method from the created object.

Fast track reading :

  • Method reference in java allows us to refer methods with class and method name directly.
  • Used to replace lambdas expressions having only another method call
  • Syntax CLass :: mehtodName OR Object ::methodName
Reference TypeLambda ExpressionMethod Reference
Static Method Reference(args) -> Class.staticMethod(args)Class::staticMethod
Object Method Reference(args) -> obj.instanceMethod(args))obj::instanceMethod
Instance Method Reference By Class Type(obj, args) -> obj.instanceMethod(args)Class::instanceMethod
Constructor Reference(args) -> new Class(args))Class::new

Introduction to Java 8 Lambda expression

Java 8 Lambda expression (or function) is an anonymous function. Anonymous function means the function who do not have name and it is not bound to any class.
Lambda expression is the shorter form of method writing. Using java 8 lambda expression we can remove the obvious and redundant code from functional interface implementation. Compiler can assume code such as : Class name, method signature, Argument types, return keyword, etc. In this blog we will learn with various Java 8 Lambda expression implementation examples.
There are many other changes in java 8 which you should read about java 8 features with examples.

Java Lambda Expression Syntax

Java Lambda expressions are very simple and contains three parts. Parameters(method arguments), arrow operator and expressions(method body).
Syntax: (parameters) -> { statements; }

As any java function we can have any number of parameters. We can also have any number of lines or expressions in body.

Example :

//Normal function
Public int add(int a, int b) {

Return a+b;

}

//Equivalent Java Lambda Expression example
(a,b) -> a+b;

For better understanding lets have look at real life example of Runnable class.

Before java 8, for any functional interface or any class object, we has either implement the class or use anonymous inner class. But after lambda we can remove this overhead.

// before java 8 with implementation
public class RunnableImpl implements Runnable {

public void run(){

System.out.println("Runnable implementation");

}

}
usage == >
Runnable runnableObject = new RunnableImpl ();

//Anonymous class implementation
Runnable runnableObject = new Runnable() {

@Override

public void run() {

System.out.println("Anonymous implementation");

}

}

Let create Lambda expression for equivalent functionality.

As we know Runnable interface is having only abstract method. For every method, signature is always fixed as defined in interface, so we can remove it.
Only the method body will be different as per the requirement so we have to write it

Runnable runnableObject = new Runnable() {      // for Runnable it obviously new Runnable()

@Override

public void run(){       // Method syntax is fix

System.out.println("Anonymous implementation");

}

The equivalent java 8 lambda expression example is,

Runnable runnableObject = () -> { System.out.println("Anonymous implementation") };

This is very basic example, however in real time applications we need to create this many times for threads, comparators, action listeners, etc. This makes code ugly. We can solve this using Java 8 lambda feature.

Things to know about Java lambda expressions:

  • Java 8 lambda expression can have zero, one or any number of arguments [eg: () -> 10; a -> a*a; (a, b) -> a + b; ]
  • For zero or more than one arguments parentheses are mandatory [eg: () -> 10; a -> a*a; (a, b) -> a + b; ]
  • For one argument parentheses are optional [eg: a -> a*a; OR (a) -> a* b; ]
  • Arguments type can be declared or auto detected [eg: (int a, int b) -> a + b; OR (a, b) -> a + b; ]
  • If we want to declare argument types, parentheses are mandatory [eg: a -> a * a; (int a) -> a*a; ]
  • For single line of expression curly brackets are optional [eg: a -> a*a; OR a -> { return a*a }; ]
  • For single line of expression return keyword is optional [eg: a -> a*a; OR a -> { return a*a }; ]
  • If we add curly brackets return keyword is required [eg: a -> a*a; OR a -> { return a*a }; ]
  • Method or class level variables can be used in Lambda expression
  • Local variables used in Java 8 lambda expressions must be effective final variables 

Any variable once initialized and whose value never changes, is called as effectively final variable. For more details about effectively final variable effectively-final in java OR effectively-final by oracle community

Lambda expression Rules

DescriptionSyntaxExample
Multiple parameters and multiple lines in body(param1,param2) -> { statement1;statement2; }(a,b,c) -> { int sum= a+b; return sum*c }
Single parameter and multiple lines in bodyparameter -> { statement1;statement2; } id -> { int sqr=n*n; return sqr;}
Single parameter and single Line expressionparameter -> expressionn-> n*n;
Multiple parameters and single Line expression(param1,param2) -> expression(a,b) -> a+b;
no parameter and single Line expression() -> expression() -> "success";
no parameter and multiple Line expressions() -> { statement1;statement2; }() -> { String name = getUserName(); print(name);}

Java 8 Lambda Expression Usage

Some most common usage of java lambda expressions are in stream and optional APIs.

Filter on stream or optional object

Filter is very basic and frequent operation that allows to filter data with some conditions.

//Filter in optional [more details]
Optional nameOptional = Optional.of(“stacktraceguru”);
Optional output = nameOptional.filter(value -> value.equals(“Java 8”));
//Filter in stream [more details]
List dataList = ...
dataList.stream().filter(d -> d.length() > 5);
}

Map on stream or optional object
Map operation is used to transform object from one form to other. This is very useful method in stream and optional

//Map in optional [more details]
Optional person = ...
Optional name = person.map(o -> o.getName());
//Map in stream [more details]
List person = ...
List names = person.stream().map(o -> o.getName()).collect(Collectors.toList());
}

There are many usage of java lambda expression examples . In real time development we can use lambda expression for many times.

More java lambda expression examples of how to write for any function

Without LambdaWith LambdaDescription
Int add(int a, int b){
return a+b;
}
(a,b) -> a+b;Function for returning addition of two numbers
Int square(int a){
return a*a;
}
 a -> a*aFunction for calculating square of number
Void print(String message){
System.out.println (message)
}
(msg)->System.out.println(msg)Function for printing message on console
Void test(){
System.out.println ("working")
}
() -> System.out.println ("working")Function for printing test message

Fast track reading :

  • Java 8 Lambda expression (or function) is an anonymous function
  • Removes the obvious code from implementation
  • Syntax: (parameters) -> { statements; }
  • Can have any number of parameters and any number of lines or expressions in body
  • For one arguments parentheses are optional
  • For single line of expression curly brackets and return keywords is optional
  • Local variables used in lambdas expressions must be effective final variables

Introduction

Optional class was added in Java 8 release under java.util package. For more details about Optional please visit Java 8 Optional page. Optional class has a private constructor so there are different ways of creating optional object. In most of the cases we just need to Check value or access value from optional object. Some times we need to perform some actions on optional object if desired value is present or we need to convert value form one form to another.

Different actions to perform on the java 8 Optional object

  1. Conditional Action With void ifPresent(Consumer<? super T> consumer): 

    Public method that executes only if value is present.
    Without optional we need to check values as,

    if(name != null){

    System.out.println("Hello" +name);

    }

    if(id != null){

    userService.findById(id);

    }

    It is very obvious that this could cause NullPointerException at runtime if we miss to check null value.

    Using optional we can overcome this issue with very clean code.

    Optional optional = Optional.of("stacktraceguru");
    optional.ifPresent( name -> System.out.println( "Hello" +name ) );
    Optional optional = Optional.ofNullable(id);
    optional.ifPresent( id -> userService.findById(id) );

    We can convert our object into Optional object using ofNullable(value) if we don’t have optional object

  2. Transformation using Optional<U> map(Function<? super T, ? extends U> mapper): 

    Public method that allows to transform value from one form to other form using specified logic.
    Most important point to note is that map() returns the new value and does not modify the original value.
    We need to provide mapping logic as argument to convert value.
    Returns the empty optional if value is not present.

    Without optional we need to check values as,

    User person = .....
    if(person!=null)

     

    name = person.getName();

    }

    Using optional we can do this easily

    Optional<User> person = .....
    Optional <String> name = person.map(o -> o.getName());

     

    OR

    Optional <String> name = person.map(User :: getName);

    We can convert our object into Optional object using ofNullable(value) if we don’t have optional object

  3. Transformation using Optional<U> flatMap(Function<? super T, Optional> mapper): 

    Public method that almost similar to map(), The difference is that map transforms values form Optional object and flatMap transforms the nested Optional object(Optional< Optional >).

    Some time while writing some programs it is possible that the Optional value is also Optional so this could lead to Optional< Optional>.

    class Testuser {

    Optional<Integer> id;

    String name;

    Testuser(Integer id, String name) {

    this.id = Optional.ofNullable(id);

    this.name = name;

    }

    //getter methods

    }

    .....

    public static void main(String[] args) {

    Optional<Testuser> optional = Optional.ofNullable( new Testuser(1, "a") );

    System.out.println ( optional );                                        // Optional[package.Testuser @ 45abcd123]

    System.out.println ( optional.map (Testuser::getId) );                  // Optional[ Optional [1] ]

    System.out.println ( optional.flatMap(Testuser::getId) );               // Optional[1]

    System.out.println ( optional.flatMap (Testuser::getName) );        // error-as nested object expected

    }

Most important point to note is the mapper function(login in argument) must return Optional object, we can not user function which does not return optional object.

Fast track reading :

  • Three  major actions can be performed optional object
  • Conditional void ifPresent (Consumer<? super T> consumer): executes the logic only if value is present
  • Transformation using Optional<U> map(Function<? super T, ? extends U> mapper): transform value from one form to other using specified logic in mapper function
  • Transformation using Optional<U> flatMap(Function<? super T, Optional> mapper): transform value from nested Optional object using specified logic in mapper function
  • Return type of Mapper function in flatMap() must be Optional

Introduction

Optional class was added in Java 8 release under java.util package, for more details about Optional please visit Java 8 Optional page. Optional class has a private constructor so there are different ways to creating optional object. In most of the cases we just need to Check value in Optional class, Some times just checking if the value is present or not is not enough, we need to access value from optional object.

There are different ways to access value from the Optional object.

Ways to access value from optional object

  1. Using public T get() method: 

    Public method that returns the value if the Optional is not empty.
    However we must be careful about the fact that if value is not present that is empty optional it throws NoSuchElementException. We should use this method on optional object only if we are sure that value is present and it is not empty optional.

    System.out.println(Optional.empty().get());
    Exception in thread "main" java.util.NoSuchElementException: No value present
    Optional<String> optional = Optional.of(“stacktraceguru”);
    System.out.println(optional .get());
    Output : stacktraceguru

    If value in optional can be null then we can use other methods like orElse(...) to access the value

  2. Using public T orElse(T other) method: 

    Public method that returns the value if the Optional is not empty.

    Optional optional= Optional.ofNullable("stacktraceguru");
    System.out.println(optional.orElse("Else value"));
    Output : stacktraceguru

    In above case the optional is not empty so output is value which is "stacktraceguru".

    Unlike get() method  we can specify value to be returned if empty Optional so it does not throws NoSuchElementException. This is most common and most used way to access value from optional object.

    Optional<String> optional = Optional.empty();
    System.out.println(optional.orElse("Else value"));
    Output : Else value
    Optional<String> optional = Optional.ofNullable(null);
    System.out.println(optional.orElse("Else value"));
    Output : Else value

    In both of the above case it is the empty optional so output is else argument which is "Else value"

  3. Using public T orElseGet (Supplier<? extends T> supplier) method: 

    Public method that almost similar to orElse with small difference, Unlike orElse(..) method  orElseGet  takes the supplier function as argument and this function will be executed if Optional is empty.
    This returns the value returned by supplier if Optional is empty.

    public String getDefaultValue(){

    return "Default value";

    }
    ...
    Optional<String> optional = Optional.empty();
    System.out.println(optional.orElseGet(()->getDefaultValue());
    Output : Default value

  4. Using public T orElseThrow (Supplier exceptionSupplier) method: 

    Public method which throws the exception if Optional is empty. We can use this method to throw custom exception.

    // before java 8
    public string getDateOfBirth(){ 

    Date dob= ..... // variable we need to check

    if(dob ==null)

    throw new Exception("Date not found");// if null throw exception

    else

    return dob; // else return value from variable

    }

    // using java 8
    public string getDateOfBirth(){ 

    Optional <Date> dob= ..... // optional variable

    return dob.orElseTrhow(() -> new Exception("Date not found") );

    }

Fast track reading :

      • Four (4) different methods to access value from optional object
      • public T get() : returns the value if present, if empty throws NoSuchElementException
    • We should use get() only when we are sure that the value is present
    • public T orElse(T other) : returns the value if present else return ‘other’ value. Used to specify default value for empty Optional
    • public T orElseGet( Supplier<? extends T> other) : returns the value if present else returns the value returned from other supplier function.
    • public T orElseThrow(Supplier exceptionSupplier) : returns the value if present else throws exception.

Introduction

Optional class was added in Java 8 release under java.util package. It works as container or wrapper for actual value which may or may not be null. This helps to avoid and handle null pointer exception in cleaner way. It also provides methods to check value in optional object. If you are new to Optional please visit Java 8 Optional page.

A noteworthy point about Optional class is that it has private constructor. We can not create object using new keyword, for more details  visit creating optional object page.

While writing code most of the times we need to filter or check value in optional object. Some times we need to check if optional contains the expected value or not, we can check if that in mainly 2 ways 

Syntax

  1. Using public boolean isPresent() method:

    public method which returns true or false depending on value present or not

    Optional<String> emptyOptional = Optional.empty();
    System.out.println(emptyOptional.isPresent());
    Output : false
    Optional<String> nameOptional = Optional.of("stacktraceguru");
    System.out.println(nameOptional.isPresent());
    Output : true
  2. Using public Optional<T> filter(Predicate<? super T> predicate) method:

    This method takes a predicate as input parameter. Predicate here is a condition that is checked with respect to optional object. It returns the optional object with value if condition matches else returns empty optional

    Check following examples for more details,

    Optional nameOptional = Optional.of("stacktraceguru");
    Optional output = nameOptional.filter(value -> value.equals("stacktraceguru"));
    Output : Optional[stacktraceguru]

    In above example we try to check optional contains the value as “stacktraceguru”. As this is correct the output is optional with “stacktraceguru” as value.

    Optional nameOptional = Optional.of("stacktraceguru");
    Optional output = nameOptional.filter(value -> value.equals("Java 8"));
    Output : Optional.empty

    In above example we try to check if optional contains the value as “Java 8”. As this is not correct the output is empty optional with no value.

    We can have any predicate function for filter which should return Boolean value, lets see some more filter examples for better understanding

    Optional nameOptional = Optional.of("stacktraceguru");
    Optional output = nameOptional.filter(value -> value.length() > 5));
    Output : Optional[stacktraceguru]

    In above example we try to check if optional value has length greater than 5. As this is true the output is optional with “stacktraceguru” as value

    Optional nameOptional = Optional.of("stacktraceguru");
    Optional output = nameOptional.filter(value -> value.length() > 15));
    Output : Optional.empty

    In above example we try to check if optional value has length greater than 15. As this is not correct the output is empty optional with no value.

Fast track reading :

  • isPresent() used to check if value present in optional or not
  • filter(...) used to filter value by some condition
  • filter(...) returns the optional of value if condition matches else returns empty optional
  • We can have any predicate function for filter which should return Boolean value

Introduction

Optional class was added in Java 8 release under java.util package. It works as container or wrapper for actual value which may or may not have null. This helps to avoid and handle null pointer exception in cleaner way. For more details about Optional please visit Java 8 Optional page

A noteworthy point about Optional class is that it has private constructor. So we can not create optional object using new keyword. Also we can not change the value in Optional once created. Hence we need to provide the value at the time of object creation

Syntax for creating optional object

There are 3 ways to create Optional object. Using 3 different static methods provided in Optional class

  1. Using public static Optional empty() method:

    Returns the Optional instance with out null value. Optional object created using this method will be always empty

    Optional<String> emptyOptional = Optional.empty();

    Empty Optional is used to represent null value. On this object we can perform some operation without null pointer exception

  2. Using public static Optional of(T value) method:

    Whenever we need to create Optional of some value, We can use Optional.of(value) to create Optional of desired value.
    In this method null value is not allowed.
    If we try to create object with null value it throws NullPointerException.

    String name ="stacktraceguru";
    Optional<String> nameOptional = Optional.of(name );
    String name = null;
    Optional<String> nameOptional = Optional.of(name );    //Wrong

    There can be some cases when we are not sure if the value will be present or not. In such cases we should use ofNullable(value) than of(value) to avoid NullPoiterException

  3. Using public static Optional ofNullable(T value) method:

    Whenever we need to create Optional of some value and if value can be null, we should use Optional.ofNullabe(value).  This create Optional of desired value or empty if null.
    In this method null value is allowed.
    If we try to create object with null value it returns empty Optional.

    String name ="stacktraceguru";
    Optional<String> nameOptional = Optional.of(name );
    String name = null;
    Optional<String> nameOptional = Optional.of(name );    //Valid
     

Fast track reading :

    • Optional class has private constructor so we can not create object using new keyword.
    • Optional value can not be changed once created
  • Three ways to create Optional class object
Create instanceArgumentsSummary
empty()                                -Null value option is created
of(T value)Value to be set - can not be nullOptional of provided not null value
ofNullable(T value)Value to be set - can be nullOptional of value, If value is not null else empty Optional

Introduction

Java 8 Optional class was added in Java 8 release under java.util package. Java 8 Optional is used to represent Optional object or empty value instead null reference.

This will help to avoid null pointer exception which occurs when we try to perform some operation on null reference object. It works as container or wrapper for actual value which may or may not have null. We will see how to use java optional with example.

Why java.util.Optional?

While developing any application or program every developer has faced NullPointerException. Consequently, in order to avoid that null pointer we need to add null checks which could require nesting of if statements. As a result leading to ugly and unreadable code.

Let's look at the following example for better insight:

This code could lead to null pointer if person or person's account does not exist

Double balance = person.getAccount().getBalance();

we can handle this in the following ways, we shall get into details of second way later in this blog.

//Traditional method of handling null
Double balance = 0.0;
if(person != null ){ 

Account account = person.getAccount();

if(account != null){

balance = account.getBalance();

}

}

// java 8 optional example to handle null
Double balance = person.flatMap(Person::getAccount) 

.flatMap(Account::getBalance)

.orElse(0.0);

Up till now we have understood that Optional class helps in avoiding null pointer scenarios. We will learn about different operations we can perform on java 8 Optional object with example.

Methods in Java 8 Optional class

Now, let us look at the different methods present in Optional class, for more details click on the links

A) Ways to create optional:

A most noteworthy point about Optional class is that it has private constructor so we can not create object using new keyword.

First of all, we can create object of Optional class in either of the 3 different ways

  1. empty(): static method which returns empty optional instance
  2. of(T value) : static method which returns optional instance of specified value
  3. ofNullable(T value) : static method which returns optional instance of specified value if value is not null else returns empty instance

B) Ways to check value in optional:

Some times we need to check if optional contains the expected value or not, we can check if optional object contains value or not in 2 ways

  1. isPresent() : public method which returns true or false depending on value present or not
  2. filter(Predicate<? super T> predicate): public method which returns optional of value if matches the condition else gives empty optional

C) Ways to get value:

Optional class provides different ways to get values depending on requirement and scenario

  1. get() : public method which returns the value if present. throws NoSuchElementException if empty
  2. orElse(T other) : public method which returns the value if present else return 'other' value. Used to specify default value or null if no value present
  3. orElseGet( Supplier<? extends T> other) : public method which returns the value if present else returns the return value of other. other is functional interface
  4. orElseThrow(Supplier exceptionSupplier) : public method which returns the value if present else throws exception. used to throw custom exception if value not present

D) Action if data present:

There are many scenarios when we need to perform some operation on data

  1. ifPresent( Consumer<? super T> consumer): public method used to perform some operation if data is present
  2. map( Function<? super  T,? extends U> mapper): public method is used for mapping of object from one form to another if not empty, returns the optional of new value
  3. flatMap( Function<? super T Optional<U>> mapper): public method is used for mapping of object from one form to another if not empty however it avoid the nesting of optional

E) Other methods in Optional:

  1. equals(Object obj):  Compares 2 Optional objects, returns true if they are equal else returns false.
  2. hashCode():Returns the hash code value of the value if present or 0 (zero) if no value is present.
  3. toString():Returns a non-empty string representation of this Optional object

Fast track reading :

  • Added in Java 8 release under java.util package.
  • Represent Optional object or empty value instead null reference.
  • Optional class has private constructor so we can not create object using new keyword.
Create instanceCheck valueFetch valueOperational methods
empty()isPresent()get()ifPresent(Consumer consumer)
of(T value)filter(Predicate predicate)orElse(T other)map(Function mapper)
ofNullable(T value) orElseGet(Supplier other)flatMap(Function mapper)
  orElseThrow(Supplier exception) 

Introduction to Java Functional SAM Interface

Interfaces having exact one abstract method are known as functional interfaces. Also known as Single Abstract Method Interfaces (SAM Interface).

Single abstract method interface means, this interface must have one abstract method. However there can be any number of default methods.

First of all this is not new concept of Java 8 however, new annotation @FunctionalInterface is added in java 8 features.

Adding @FunctionalInterface annotation enforces that the interface has exact one abstract method. While adding @FunctionalInterface annotation is not mandatory.

Functional interface annotation enforces the rule of single responsibility. [more about Single responsibility principle]

Usage and Syntax of Java 8 Functional SAM Interface

We can use functional interface like any other interface in java.

However from java 8 functional interfaces are very important, because we use functional interface for implementing Lambda expressions. You can read more about lambda expression here.

For writing lambda expression we mostly use functional interface. Due to many functional interfaces newly added in java 8, we don’t have to write a new functional interface while writing lambda expressions.

Java.util.function Package contains newly added functional interfaces which are commonly used.

Following are the valid java functional(SAM) interface examples:

@FunctionalInterface
public interface MyFunctionalInterface {

public void abstractMethod();

}

public interface MyFunctionalInterface {

public void abstractMethod();

}

As the rule suggests, functional interface must have exact one abstract method.

In contrast we can add as many default methods as per our requirements, because default methods are not abstract. This is the reason why some java classes are called as functional interfaces even if it has many methods.

For example, Java Comparator class is a functional interface even though it has 7 default methods.

@FunctionalInterface
public interface MyFunctionalInterface {

public void abstractMethod();

   // Allowed to add default methods

default void print(){

System.out.println(“Default Method 1“);

}

default void print2(){

System.out.println(“Default Method 2“);

}

}

package java.util;
...
@FunctionalInterface
public interface Comparator {

int compare(T var1, T var2);

   // Allowed to add default methods

default Comparator reversed() {

return Collections.reverseOrder(this);

}

...
}

Overriding java.lang.Object class method as abstract

Overriding and making a method from java.lang.Object class as abstract in our sam functional interface , also does not count toward the interface’s abstract method. Because any implementing class will always have implementation inherited from java.lang.Object or elsewhere.

@FunctionalInterface                             // valid java 8 SAM interface example
public interface MyFunctionalInterface {

void abstractMethod();

boolean equals(Object obj);         //Because not considered as functional interface abstract method

public String toString();          //Because not considered as functional interface abstract method

}

For example Java Comparator is a functional interface But, it overrides equals() as abstract method

package java.util;                           //VALID
...
@FunctionalInterface
public interface Comparator {

int compare(T var1, T var2);

boolean equals(Object var1);

....

}

Java.util.function Package

As discussed many functional interfaces are added in java 8 java.util.function package. Let's understand more about function package of java 8. These new classes are also known as SAM interfaces.

The SAM interfaces in Java.util.function package are manly categorized into 4 types

  1. Consumer: These interfaces have a method which takes input, but does not return anything
    Syntax: Consumer<InputType> object = variable -> method body;
    Example: Consumer<String> print = str -> System.out.println(str);
  2. Supplier : In contrast, these interfaces have a method which returns something without taking any input
    Syntax: Supplier<ReturnType> object = ()-> method body;
    Example: Supplier<Long> useIdSupplier = ()-> userDao.getMaxId()+1;
  3. Predicates : These interfaces have a method which takes input and returns boolean value
    Syntax: Predicate<InputType> object = variable -> method body;
    Example: Predicate<Long> evenChecker = n->n%2==0;
  4. Function : These interfaces have a method which takes input and returns output
    Syntax: Function<InputType,ReturnType> object = variable -> method body;
    Example: Function<Long, Long> square = n -> n*n;

Each of the above 4 categories has provided variety of special interfaces for most common primitive data types.

We can use Consumer, Supplier, Predicate or Function interfaces with wrapper classes, however using a specific interface makes code more cleaner and easy. Let's see the different variants:

  • All of the 4 categories provide special interfaces for int, long and double, However Supplier provides an extra interface for boolean
    • IntConsumer, LongConsumer , DoubleConsumer
    • IntSupplier, LongSupplier, DoubleSupplier, BooleanSupplier
    • IntPredicate, LongPredicate, DoublePredicate
    • IntFunction<R>, LongFunction<R>, DoubleFunction<R>
  • Function provides even more special functions for converting values int, long and double. Name of the classes are self-explanatory
    • ToIntFunction, ToLongFunction, ToDoubleFunction
    • IntToLongFunction, LongToIntFunction
    • IntToDoubleFunction, DoubleToIntFunction
    • LongToDoubleFunction, DoubleToLongFunction
  • Consumer, Predicate and Function have variants that accept 2 input arguments. Supplier doesn't accept any arguments so it does not have any class of this type
    • BiConsumer<T,U> Accepts two input arguments of type T and U , returns nothing.
    • BiPredicate<T,U> Accepts two input arguments of type T and U , returns boolean value.
    • BiFunction<T,U,R> Accepts two input arguments of type T and U , returns a result of type R.
  • BiConsumer has 3 even more variants which accept one object and other primitive (int long or double)
    • ObjIntConsumer Accepts object of t type and int
    • ObjLongConsumer Accepts object of t type and Long
    • ObjDoubleConsumer Accepts object of t type and Double
  • BiFunction has 3 more variants for accepting 2 arguments and convert to primitive (int long or double)
    • ToIntBiFunction<T,U> Accepts two input arguments of type T and U , returns int result.
    • ToLongBiFunction<T,U> Accepts two input arguments of type T and U , returns Long result.
    • ToDoubleBiFunction<T,U> Accepts two input arguments of type T and U , returns Double.
  • In addition Function provides more types that accept and return object of same type
    • UnaryOperator Accepts single operand that returns a result of same type.
    • IntUnaryOperator Accepts a int value and returns int result.
    • LongUnaryOperator Accepts a Long value and returns int result.
    • DoubleUnaryOperator Accepts a Double value and returns int result.
    • BinaryOperator Accepts two parameter of same type and returns result of same type.
    • IntBinaryOperator Accepts two int value and returns int result.
    • LongBinaryOperator Accepts two Long value and returns Long result
    • DoubleBinaryOperator Accepts two Double value and returns Double result

Fast track reading :

    • Also known as Single Abstract Method interfaces (SAM Interface)
    • Exactly one abstract method is allowed
    • @FunctionalInterface is optional and it is valid even if annotation would be omitted.
    • Default methods are not abstract so we can add any number of default methods to the functional interface
    • Allows overriding Object class methods as abstract
    • Functional interfaces are very important for writing lambda expression
  • Many functional interfaces are added in java 8 java.util.function package
TypeConsumerSupplierPredicateFunction
SummaryAccepts somethingProduces somethingProcess and return true/falseAccepts something and produces something
InputParameter  No InputParameterParameter
Returns  -ResultAlways booleanResult
primitive variantsIntConsumer
LongConsumer
DoubleCnsumer
IntSupplier
LongSupplier
DoubleSupplier
BooleanSupplier
IntPredicate
LongPredicate
DoublePredicate
IntFunction<R>
LongFunction<R>
DoubleFunction<R>
For conversion  -  -  -ToIntFunction<T>
ToLongFunction<T>
ToDoubleFunction<T>


IntToLongFunction
LongToIntFunction


IntToDoubleFunction
DoubleToIntFunction


LongToDoubleFunction
DoubleToLongFunction

For 2 inputsBiConsumer<T,U>  -BiPredicate<T,U>BiFunction<T,U,R>
2 input variantsObjIntConsumer<T>
ObjLongConsumer<T>
ObjDoubleConsumer<T>
  -  -ToIntBiFunction<T,U>
ToLongBiFunction<T,U>
ToDoubleBiFunction<T,U>
Accept and return same type  -  -  -UnaryOperator
IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator


BinaryOperator
IntBinaryOperator
LongBinaryOperator
DoubleBinaryOperator