Java 8
Functional Interfaces
Functional interface is an interface with SAM(Single Abstract Method) also with multiple default and static methods, is known as functional interface.
Few default functional interfaces in java
Runnable -> run()
Callable -> call()
Comparable -> compareTo(T o)
Comparator -> compare(T o1,T o2)
Consumer -> accept(T t)
Predicate -> test(T t)
Supplier -> get()
Consumer Interface
Consumer can be used in all contexts where an object needs to be consumed, i.e taken as input and some operation is to be performed on the object without returning any result.In other words Consumer is a functional interface which accepts a single input and returns no output
.
Consumer functional interface has two methods.
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after);
Consumer accept() and andThen() method example
- accept() : takes one input does the operation and has no return type.
- andThen(): helper tool,to join multiple consumers instead of passing all of them in a loop, we use andThen to chain the logic of two consumers.
public class User {
private String id;
private String name;
private int age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User [age="+ age+"id: "+id+"name: "+name+"]";
}
}
public static void main(String args[]) {
List<User> userList = new ArrayList<>();
userList.add(new User("1", "vinay", 22));
userList.add(new User("2", "vinay", 22));
userList.add(new User("3", "vinay", 22));
userList.add(new User("4", "vinay", 22));
//accept() method accept one value and does one operation.
Consumer<List<User>> consumerUser = t -> System.out.println(t);
consumerUser.accept(userList);
//consumer andThen() method example
Consumer<User> name = t -> System.out.println("Name: "+t.getName());
Consumer<User> age = t -> System.out.println(" Age: "+t.getAge());
Consumer<User> id = t -> System.out.println("Id:"+t.getId());
Consumer<User> result= name.andThen(age).andThen(id);
User user = new User("1", "Alpha", 60);
result.accept(user);
}
[User [age=22id: 1name: vinay], User [age=22id: 2name: vinay], User [age=22id: 3name: vinay], User [age=22id: 4name: vinay]]
Name: Alpha
Age: 60
Id:1
Predicate Interface
Functional interface used for conditional check,mainly used to filter data from java streams.We can use this when ever we want to check something and return true or false based on condition.
Predicate has only one abstract method that is test()
boolean test(T t);
Mainly used to filter data from java stream,the filter()
method of a stream accepts a predicate to filter the data and returns a new stream satisfying the predicate.
Examples like find the list of users whose age is greater than 20, whose name starts with ‘A’ etc.
Predicate<User> userAge= t -> t.getAge()>15;
userList.stream().filter(userAge).forEach(t -> System.out.println(t));
The filter()
method in streams takes input a predicate,for the above User
POJO,let us implement a predicate example where, we return a boolean
based on the condition.Here if the user age is greater than 15, the user is returned.
Output:
User [age=22id: 1name: vinay]
User [age=22id: 2name: vinay]
User [age=22id: 3name: vinay]
User [age=22id: 4name: vinay]
Supplier Interface
The supplier interface takes no argument and returns a result, as this is functional interface can be used as assignment target as lambda expression,method reference etc. Supplier interface has only one get() method.
Supplier<User> userSupplier = () -> new User("1", "vinay", 10);
System.out.println(userSupplier.get());
// For the same above User POJO,Output:
User [age=10id: 1name: vinay]
Function Interface
The function is a functional interface,for which Function<T, R> has been created for mapping scenarios i.e when an object of a type is taken as input (T) and it is converted to another type
T
: denotes the input arguments, R
: denotes the return type.
The Function interface consists of the following 4 methods as listed which are later discussed as follows: apply() andThen() compose() identity()
Function<List<User>, Integer> func = t -> t.size();
Integer size= func.apply(userList);
System.out.println(size);
Function<List<User>, Integer> func = t -> t.size();
func= func.andThen(t -> t*3);
func = func.andThen(t -> t+3);
System.out.println(func.apply(userList)); //output 15 because the size from the above userList is 4
Lambda Expressions
Lambda expression provides implementation of a interface which has functional interface.
What is Functional interface? - An interface which has only one abstract method is called functional interface.
Lambda expressions, takes in parameters and returns a value,Lambda expressions are similar to methods, but they do not need a name and they can be implemented in the body of the method.The body cannot contain variables,assignments, statements such as if
, for
.
Drawback of Lambda Expressions - they can only used with functional interfaces.
(parameter_list) -> { body of lambda expressions }
ArrayList<Integer> arrL = new ArrayList<>();
arrL.add(10);
arrL.add(20);
arrL.add(30);
arrL.forEach(t -> System.out.println(t));
// printing only even numbers
// if multiple statements are present within the body `{}` statements should be defined within curly braces
arrL.forEach(t -> {
if (t % 2 == 0)
System.out.println(t);
});
Method References
- Reference to a static method
Object::staticMethodName
- Reference to a instance method
can refer to an instance method, after creating a object for the class and then calling instance method name.
// instantiate the class first and then reference the object.
containingObject::instanceMethodName
- Reference to a constructor
ClassName::new
Streams
Stream API is used to process collections of objects,Streams are designed to be efficient and support improving performance for the program, to avoid unnecessary loops,iterations.Streams can be used for filtering,collecting,printing,converting from one data structure to another.
Features of Streams:
- Stream is not a data structure instead it takes input from collections,arrays, I/O channels.
- Streams doesn’t change the original data structure,they only provide the result as per the pipelined methods.
- Each intermediate operations is lazily executed and returns stream as result,hence intermediate operations can be pipelined.Terminal operations mark the end of the stream and return the result.
How Streams works internally:
[Stream Source] <- (create stream instance) [Operation1,operation2..] -> (Terminal Operation) [Operation Result]
- To filter out objects we have a function called filter()
- To impose a condition we have logic of predicate,functional interface can be replaced by expression[[I
- To collect elements we will be using Collectors.toList() - to collect all the required elements.
Core Operations over streams:
1. Intermediate operations.
2. Terminal operations.
3. Short circuit operations.
Intermediate Operations: return the stream itself so can chain multiple intermediate operations, in a row like
- `filter()`: Filters element based on condition
- `map()`: Transforms each element in a stream to another value
- ` sorted()`: sort the elements of a stream
Terminal Operations: on execution return a final result as an absolute value.
- `collect()`: It is used to return the result of the intermediate operations performed on the stream.
- `forEach()`: Iterates all element in stream
- `reduce()`: Reduce the elements of a stream to a single value.
Short circuit Operations: provide performance benefits by avoiding unnecessary computations when the desired results can be obtained early.
- `anyMatch()`: checks the stream if it satisfies the given condition.
- `findFirst()`: it checks the element that matches the condition and stops processing when it is found.
Parallel vs Sequential Stream in java
Sequential Streams: Sequential streams are non parallel streams that uses a single thread to process the pipelining, any stream operation without explicitly specified as parallel is treated as sequential stream.Sequential stream objects are pipelined in a single stream on the same processing system,hence it never takes advantage of the multi processing system.Sequential streams performs operations one by one.
stream()
method returns a sequential stream in java.
Parallel Streams: To leverage multi core processors,which increases the performance, using parallel streams our code gets divided into multiple streams,and these can be executed parallely on separate cores on the system.
parallelStream()
: method returns