Monday, September 7, 2020

Java 8 Features: Lambda expression & Method reference to an instance method of a particular object- Class Instance::instance Method Name

Lambda expression is a new which is introduced in Java 8. A lambda expression is an anonymous function.

A function that doesn’t have a name and doesn’t belong to any class. The concept of lambda expression was first introduced in LISP programming language.

Where to use the Lambdas in Java

To use lambda expression, you need to either create your own functional interface or use the pre defined functional interface provided by Java. An interface with only single abstract method is called functional interface(or Single Abstract method interface), for example: Runnable, callable, ActionListener etc.

Lambda expression vs method in Java

A method (or function) in Java has these main parts:
    1.  Name
  1. Parameter list
  2. Body
    4.  return type.

A lambda expression in Java has these main parts:Lambda expression only has body and parameter list.

1. No name – function is anonymous so we don’t care about the name

2. Parameter list

3. Body – This is the main part of the function.

4. No return type – The java 8 compiler is able to infer the return type by checking the code. you need not to mention it explicitly.

Java Lambda Expression Syntax

To create a lambda expression, we specify input parameters (if there are any) on the left side of the lambda operator ->, and place the expression or block of statements on the right side of lambda operator. For example, the lambda expression (x, y) -> x + y specifies that lambda expression takes two arguments x and y and returns the sum of these.

package com.cerotid.fundamentals;
interface A {
void show();

}
class Xyz implements A {
@Override
public void show() {
System.out.println("Hello");
}

}
public class LambdaDemo {
public static void main(String[] args) {
A obj = new Xyz();
obj.show();
}
}
Output: Hello

package com.cerotid.fundamentals;
interface A {
void show();
}
public class LambdaDemo {
public static void main(String[] args) {
A obj = new A() { //Anonymous Inner Class
public void show() {
System.out.println("Hello");
}
};

package com.cerotid.fundamentals;
interface A {
void show();
}
public class LambdaDemo {
public static void main(String[] args) {
A obj = () -> {
System.out.println("Hello");
};
obj.show();
}
}
  1. Functional Interfaces
  2. Default Methods
Predefined Functional interfaces
  1. Predicates
  2. Functions
  3. Double Colon Operator(Constructor and Method reference)
  4. Stream API
  5. Date and Time API (Joda Time API)


Stream in Java:

  • If we want to represent a group of objects as a single entity then we should go for collection.
  • If we want to process objects from the collection then we should go for stream

Introduced in Java 8, the Stream API is used to process collection of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result.
The features of Java stream are –

  • A stream is not a data structure instead it takes input from the Collection, Arrays or I/O channels.
  • A stream does not change the original data structure, it only provides the result as per the pipelined methods.
  • Each intermediate operation is lazily executed and returns a stream as a result, hence various intermediate operations can be pipelined. Terminal operations mark the end of the stream and return the result.
package com.cerotid.fundamentals;

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamDemo {

public static void main(String[] args) {

// create a list of integers
List<Integer> number = Arrays.asList(2,3,4,5);
// demonstration of map method
List<Integer> square = number.stream().map(x -> x*x).
collect(Collectors.toList());
System.out.println(square);
// create a list of String
List<String> names =
Arrays.asList("Reflection","Collection","Stream");
// demonstration of filter method
List<String> result = names.stream().filter(s->s.startsWith("S")).
collect(Collectors.toList());
System.out.println(result);
// demonstration of sorted method
List<String> show =
names.stream().sorted().collect(Collectors.toList());
System.out.println(show);
// create a list of integers
List<Integer> numbers = Arrays.asList(2,3,4,5,2);
// collect method returns a set
Set<Integer> squareSet =
numbers.stream().map(x->x*x).collect(Collectors.toSet());
System.out.println(squareSet);
// demonstration of forEach method
number.stream().map(x->x*x).forEach(y->System.out.println(y));
// demonstration of reduce method
int even =
number.stream().filter(x->x%2==0).reduce(0,(ans,i)-> ans+i);
System.out.println(even);
}

}

Output:
[4, 9, 16, 25]
[Stream]
[Collection, Reflection, Stream]
[16, 4, 9, 25]
4
9
16
25
6

In this section we will use the following List as a source for all streams:
List<Product> productList = Arrays.asList(new Product(23, "potatoes"),
new Product(14, "orange"), new Product(13, "lemon"),
new Product(23, "bread"), new Product(13, "sugar"));
Converting a stream to the Collection (Collection, List or Set):
List<String> collectorCollection =
productList.stream().map(Product::getName).collect(Collectors.toList());


Finding certain strings without using Stream
import java.util.ArrayList;
import java.util.List;
public class Example{
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
names.add("Ajeet");
names.add("Negan");
names.add("Aditya");
names.add("Steve");
int count = 0;
for (String str : names) {
if (str.length() < 6)
count++;
}
System.out.println("There are "+count+" strings with length less than 6");
}
}
Output:

There are 3 strings with length less than 6
Same example using Stream
import java.util.ArrayList;
import java.util.List;
public class Example{
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
names.add("Ajeet");
names.add("Negan");
names.add("Aditya");
names.add("Steve");
//Using Stream and Lambda expression
long count = names.stream().filter(str->str.length()<6).count();
System.out.println("There are "+count+" strings with length less than 6");

}
}
Output:
There are 3 strings with length less than 6
What is the difference between these codes?The output of both the examples are same, however there is a major difference between these examples if you consider the performance of the code.In the first example, we are iterating the whole list to find the strings with length less than 6. There is no parallelism in this code.In the second example, the stream() method returns a stream of all the names, the filter() method returns another stream of names with length less than 6, the count() method reduces this stream to the result. All these operations are happening parallelly which means we are able to parallelize the code with the help of streams. Parallel execution of operations using stream is faster than sequential execution without using streams.

package com.cerotid.fundamentals;

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

public class DemoJavaStream {

public static void main(String[] args) {

ArrayList<Integer> marks = new ArrayList<Integer>();
marks.add(70);
marks.add(45);
marks.add(10);
marks.add(20);
marks.add(25);
System.out.println("...........before applying stream()..........: " + marks);

List<Integer> updatedMarks = marks.stream().map(i -> i + 5).collect(Collectors.toList());
System.out.println("..........after applying stream()...............: " + updatedMarks);

long noOfFailedStudents = marks.stream().filter(m -> m < 35).count();
System.out.println(noOfFailedStudents);

List<Integer> sortedList = marks.stream().sorted().collect(Collectors.toList());
System.out.println(sortedList);

Comparator<Integer> cmpr = (i1, i2) -> i1.compareTo(i2);
Integer minValue = marks.stream().min(cmpr).get();
System.out.println(minValue);
Integer maxValue = marks.stream().max(cmpr).get();
System.out.println(maxValue);
Consumer<Integer> consumer = i ->{
System.out.println("The square of " + i + " is : "+(i*i));
};
marks.stream().forEach(consumer);
Integer[] i = marks.stream().toArray(Integer[]::new);
System.out.println("........using for each loop...........");
for (Integer integer : i) {
System.out.println(integer);
}
System.out.println("........using forEach method..............");
Stream.of(i).forEach(System.out::println);
System.out.println("....printing pattern using forEach()............");
Stream <Integer> s = Stream.of(9, 99, 999, 9999, 99999);
s.forEach(System.out::println);
System.out.println(".......print array using forEach method.........");
Integer [] intArray = {10, 20, 30, 40, 50};
Stream.of(intArray).forEach(System.out::println);
Comparator<String> comparator = (String o1, String o2) -> {
return o1.compareTo(o2);
};

String s1 = "MY nname";
String s2 = "MY nnamq";
System.out.println("Comparing");
System.out.println(comparator.compare(s1, s2));

}
}
output:
...........before applying stream()..........: [70, 45, 10, 20, 25]
..........after applying stream()...............: [75, 50, 15, 25, 30]
3
[10, 20, 25, 45, 70]
10
70
The square of 70 is : 4900
The square of 45 is : 2025
The square of 10 is : 100
The square of 20 is : 400
The square of 25 is : 625
........using for each loop...........
70
45
10
20
25
........using forEach method..............
70
45
10
20
25
....printing pattern using forEach()............
9
99
999
9999
99999
.......print array using forEach method.........
10
20
30
40
50
Comparing
-12

Map example:
package com.cerotid.fundamentals;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class DemoJavaStream2 {

public static void main(String[] args) {

ArrayList<String> list1 = new ArrayList<String>();
list1.add("Sunny Loene");
list1.add("Kajal Agarwal");
list1.add("Prabhas ");
list1.add("Anushka Shetty");
list1.add("Mallika Sharawat");
System.out.println("...........before applying stream()..........: " + list1);

//default natural sorting order
List <String> sortedList =list1.stream().sorted().collect(Collectors.toList());
System.out.println("..........after applying stream()...............: " + sortedList);

//customized sorting order
List<String>sortedList2 =list1.stream().sorted((s1, s2) ->s2.compareTo(s1)).collect(Collectors.toList());
System.out.println(sortedList2);
Comparator<String> comparator = (s1, s2) ->{
int l1 = s1.length();
int l2 = s2.length();
if (l1<l2)
return -1;
else if (l1>l2) {
return +1;
}
else
return s1.compareTo(s2);
};
List<String>sortedList3 =list1.stream().sorted(comparator).collect(Collectors.toList());
System.out.println(sortedList3);
}
}
Output:
...........before applying stream()..........: [Sunny Loene, Kajal Agarwal, Prabhas , Anushka Shetty, Mallika Sharawat]
..........after applying stream()...............: [Anushka Shetty, Kajal Agarwal, Mallika Sharawat, Prabhas , Sunny Loene]
[Sunny Loene, Prabhas , Mallika Sharawat, Kajal Agarwal, Anushka Shetty]
[Prabhas , Sunny Loene, Kajal Agarwal, Anushka Shetty, Mallika Sharawat]

Method References in Java 8:

Method reference is a shorthand notation of a lambda expression to call a method. For example:
str -> System.out.println(str)
then you can replace it with a method reference like this:
System.out::println

The :: operator is used in method reference to separate the class or object from the method name.

Four types of method references:

1. Method reference to an instance method of a particular object- ClassInstance::instanceMethodName

Lambda expression.
(args) -> object.instanceMethodName(args)

Method Reference.
object::instanceMethodName

package com.method.reference;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Java8MethodReference1 {
public static void main(String[] args) {
// List<Employee> employeeList = Arrays.asList(
// new Employee("Ram", 27, BigDecimal.valueOf(3990)),
// new Employee("Shyam", 56, BigDecimal.valueOf(748498)),
// new Employee("Unknown", 41, BigDecimal.valueOf(4599)));
List<Employee> employeeList = new ArrayList<>();
Employee employee = new Employee();
employee = new Employee("Ram", 28, BigDecimal.valueOf(4500));
employeeList.add(employee);
employee = new Employee("Shyam", 31, BigDecimal.valueOf(2300));
employeeList.add(employee);
employee = new Employee("Hari", 32, BigDecimal.valueOf(2100));
employeeList.add(employee);
employee = new Employee("Gita", 19, BigDecimal.valueOf(1990));
employeeList.add(employee);
employee = new Employee("Sita", 23, BigDecimal.valueOf(1400));
employeeList.add(employee);
System.out.println("\nPrinting employee info using forEach method and method reference");
// employeeList.forEach(employeeInfo -> System.out.println(employeeInfo));
employeeList.forEach(System.out::println);
ComparatorProvider comparatorProvider = new ComparatorProvider();
// using lambda expression
// (args) -> object.instanceMethodName(args)
System.out.println("\nSorted employee list by age using lambda expression");
employeeList.sort((e1, e2) -> comparatorProvider.compareByAge(e1, e2));
employeeList.forEach(sortedList -> System.out.println(sortedList));
// using method reference
// object::instanceMethodName
System.out.println("\nSorted employee list by age using method reference");
employeeList.sort(comparatorProvider::compareByAge);
employeeList.forEach(sortedList -> System.out.println(sortedList));
}
}

2. Method Reference to an instance method of an arbitrary object of a particular type

Lambda expression:
// arg0 is the first argument
(arg0, rest_of_args) -> arg0.methodName(rest_of_args)

// example, assume a and b are String
(a, b) -> a.compareToIgnoreCase(b)

Method Reference:
// first argument type
arg0_Type::methodName

// arg0 is type of ClassName
ClassName::methodName

// example, a is type of String
String::compareToIgnoreCase

For (String a, String b), where a and b are arbitrary names, and String is its arbitrary type. This example uses method reference to an instance method compareToIgnoreCase of an arbitrary object a (first argument) of a particular type String.

No comments:

Post a Comment