Streams

  1. Intermediate Operations returns Stream as output. methods like map(), sorted(), distinct() would return stream as output
  2. Terminal Operations returns something other than Stream. Collect() returns collection. Reduce() returns one element

Converting list to a stream and print them

List<Integer> arrNumbers = List.of(10,27,31, 35, 40, 44, 48, 50);
//Convert list to stream
Stream objStream = arrNumbers.stream();

//Static Method Reference
objStream.forEach(System.out::println);

Both Map and Filter returns Streams.
Filter takes predicate as input and adds element to stream based on result of predicate(I.E. True or false).
Map takes function as input and adds transformed object value to stream.
Reduce takes function as input and returns a single value that is computed going over the entire rows in stream.

Filter
Filter is used for filtering the data, it always returns the boolean value. If it returns true, the item is added to list else it is filtered out (ignored)

  1. Filter takes a predicate as argument. Predicate returns boolean. The Output of Filter is a Stream
  2. Predicate object which is technically a function to convert an object to boolean. We pass an object and it will return true or false.
  3. filter() method is lazy like map, it wont be evaluated until you call a reduction method, like collect

Using Filter to filter names in list and print them

import java.util.*;
import java.util.stream.Stream;

List<String> arrNames = List.of("Mugil", "Mani", "Madhu");
arrNames.stream()
        .filter(name -> name.equals("Mugil"))
        .forEach(System.out::println);
List<Integer> arrNumbers = List.of(10,27,31, 35, 40, 44, 48, 50);
List<String> arrSkills = List.of("Spring Boot", "Spring Security", "Java 8", "Microservices", "Spring MVC");

//Printing Even Numbers
System.out.println("------Even Numbers-------");
arrNumbers.stream().filter(FilterEgs::isEven).forEach(System.out::println);

//Printing Even Numbers
System.out.println("------Even Numbers-------");
arrNumbers.stream().filter(no -> no%2==0).forEach(System.out::println);

//Printing Odd Numbers
System.out.println("------Odd Numbers-------");
arrNumbers.stream().filter(no -> no%2==1).forEach(System.out::println);

//Printing Odd Numbers
System.out.println("------Print Spring Skills-------");
arrSkills.stream().filter(skill -> skill.toLowerCase().contains("spring"))
.forEach(System.out::println);

private static boolean isEven(int no){
        return no%2==0;
}       

Output

------Even Numbers-------
10
40
44
48
50
------Odd Numbers-------
27
31
35
------Print Spring  Skills-------
Spring Boot
Spring Security
Spring MVC

Map
By using map, you transform the object values. the map returns the transformed object value as Stream.

  1. Map takes a function as a argument and performs transform action on elements. The Output of Map is a Stream
  2. map() is used to transform one object into another by applying a function.
  3. Stream.map(Function mapper) takes a function as an argument.
  4. mapping function converts one object to the other. Then, the map() function will do the transformation for you. It is also an intermediate Stream operation, which means you can call other Stream methods, like a filter, or collect on this to create a chain of transformations.
List<Integer> arrNum = List.of(5,6,8,57,4,1,26,84,15);
List<String> arrSkills = List.of("Spring Boot", "Spring Security", "Java 8", "Microservices", "Spring MVC");


//Printing Square of Numbers whose value less than 15
System.out.println("------Square of Numbers-------");
arrNum.stream()
      .filter(num -> num<15)   
      .map(num -> num*num)
      .forEach(System.out::println);


//Print Total Characters in String
System.out.println("------Print Total Characters in String-------");
arrSkills.stream()
		.map(skill -> skill + " contains "+ skill.length()+ " Characters")
		.forEach(System.out::println);

Output

------Square of Numbers-------
25
36
64
16
1
------Print Total Characters in String-------
Spring Boot Contains 11 Characters
Spring Security Contains 4 Characters
Java 8 Contains 5 Characters

Reduce

  1. Stream.reduce() takes function as Parameter
  2. Reduction stream operations allow us to produce one single result from a sequence of elements, by repeatedly applying a combining operation to the elements in the sequence.
  3. Stream.reduce() operation Contains Identity, Accumulator and Combiner
  4. Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
  5. Accumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
  6. Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation

ReduceEgs.java

import java.util.Arrays;
import java.util.List;

public class ReduceEgs {
    public static void main(String[] args) {
        List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,6,7,8,9);

        System.out.println(arrNumbers.stream().reduce(0, ReduceEgs::sumNos));
    }

    public static Integer sumNos(int x, int y){
        System.out.println("a -"+ x + " b - "+  y);

        return x+y;
    }
}

Output

a -0 b - 1
a -1 b - 2
a -3 b - 3
a -6 b - 4
a -10 b - 5
a -15 b - 6
a -21 b - 7
a -28 b - 8
a -36 b - 9
45

In the above example

  1. the Integer value 0 is the identity. It stores the initial value of the reduction operation and also the default result when the stream of Integer values is empty.
  2. the lambda expression x+y represented by function sumNos is the accumulator since it takes the partial sum of Integer values and the next element in the stream.
  3. When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner

Role of combiner would be visibile only in parallelStream with output as below. It would be hard to interpret the output response in console.

ReduceEgs.java

.
.
 System.out.println(arrNumbers.parallelStream().reduce(0, ReduceEgs::sumNos));
.
.

Output

a -0 b - 6
a -0 b - 5
a -5 b - 6
a -0 b - 1
a -0 b - 7
a -0 b - 9
a -0 b - 2
a -1 b - 2
a -0 b - 3
a -0 b - 8
a -8 b - 9
a -0 b - 4
a -3 b - 4
a -3 b - 7
a -7 b - 17
a -11 b - 24
a -10 b - 35
45

Alternate ways to Sum Nos

public static void main(String[] args) {
 List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,6,7,8,9);

 Integer sum = arrNumbers.stream().reduce(0, Integer::sum);
 Integer sum2 = arrNumbers.stream().reduce(0, (x,y)->x+y);

 System.out.println(sum);
 System.out.println(sum2);
}

Once you get the values in stream then it should be typecast to its original type before calling its methods as values are stored as Objects in stream

Below you could see the name is typecast to String before calling lowerCase method

List<String> arrNames = Arrays.asList("Mugil", "Mani", "Vinu");
Stream arrNameStream = arrNames.stream();
arrNameStream.forEach(name -> System.out.println(((String)name).toLowerCase()));
  1. What is the Difference between Map and Filter?
    Both perform intermediate Operations and returns stream as output.By using map, you transform the object values.
    Map returns a stream consisting of the results of applying the given function to the elements of this stream. In a simple sentence, the map returns the transformed object value.
    Filter is used for filtering the data, it always returns the boolean value. If it returns true, the item is added to list else it is filtered out (ignored)
  2. Find the Sum of Nos in List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,6,7,8,9);
    
            Integer sum = arrNumbers.stream().reduce(0, Integer::sum);
            Integer sum2 = arrNumbers.stream().reduce(0, (x,y)->x+y);
            Integer sum3 = arrNumbers.stream().reduce(0, ReduceEgs::sumNos);
        }
    
        public static Integer sumNos(int x, int y){
            System.out.println("a -"+ x + " b - "+  y);
            return x+y;
        }
    }
    
  3. Find the Min and Max in Nos List?
     public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,-6,7,-8,9);
    
            arrNumbers =  Arrays.asList(-5,-6,-8);
            Integer Max = arrNumbers.stream().reduce(0, (x,y)->x>y?x:y);
            System.out.println("Output 1 ="+ Max);
    
            //Its good to start with Identity as Negative Value in order to address negative Integer Comparison
            //If you start with 0 then 0 would be returned like above
            Integer Max2 = arrNumbers.stream().reduce(Integer.MIN_VALUE, (x,y)->x>y?x:y);
            System.out.println("Output 2 ="+ Max2);
    
            arrNumbers =  Arrays.asList(5,6,8);
    
            Integer Min = arrNumbers.stream().reduce(0, (x,y)->x<y?x:y);
            System.out.println("Output 3 ="+ Min);
    
            //Its good to start with Identity as Max Positive Value in order to address Integer Comparison
            Integer Min2 = arrNumbers.stream().reduce(Integer.MAX_VALUE, (x,y)->x<y?x:y);
            System.out.println("Output 4 ="+ Min2);
        }
    

    Output

    Output 1 =0
    Output 2 =-5
    Output 3 =0
    Output 4 =5
    
  4. Find the Sum of Square of Nos in List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3);
    
            Integer squareSum = arrNumbers.stream().map(x->x*x).reduce(0, Integer::sum);
            System.out.println("Output 1 ="+ squareSum);
        }
    }
    

    Output

    Output 1 =14
    
  5. Find the Sum of Odd Nos in List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3);
    
            Integer squareSum = arrNumbers.stream().filter(x-> x%2==1).reduce(0, Integer::sum);
            System.out.println("Output 1 ="+ squareSum);
        }
    }
    

    Output

    Output 1 =4
    
  6. Distinct elements from List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3,1,4,2);
    
            System.out.println("Output 1");
            arrNumbers.stream().distinct().forEach(System.out::println);
        }
    }
    
    

    Output

    Output 1
    1
    2
    3
    4
    
  7. Sort Elements in List?
     public static void main(String[] args) {
       List<Integer> arrNumbers =  Arrays.asList(1,2,3,1,4,2);
    
       System.out.println("Output 2");
       arrNumbers.stream().sorted().forEach(System.out::println);
    }
    

    Output

    Output 2
    1
    1
    2
    2
    3
    4
    
  8. Sorting based on Natural Order, Reverse Order and Length of String?
    public static void main(String[] args) {
            List<String> arrSkills = Arrays.asList("Java", "Python",  "Ant");
    
            System.out.println("Sorting in Natural Order");
            arrSkills.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
    
            System.out.println("Sorting in Reverse Order");
            arrSkills.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
    
            System.out.println("Sorting based on Length");
            arrSkills.stream().sorted(Comparator.comparing(str -> str.length())).forEach(System.out::println);
    
        }
    

    Output

    Sorting in Natural Order
    Ant
    Java
    Python
    Sorting in Reverse Order
    Python
    Java
    Ant
    Sorting based on Length
    Ant
    Java
    Python
    
  9. Collect the Elements in the List – Collect Even Nos and Length of String ?
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);
            List<String> arrNames = Arrays.asList("Java", "Oracle", "Maven", "Ant");
    
            System.out.println("Collect Even Nos in list");
            List<Integer> arrEvenNumbers = arrNumbers.stream().filter(x->x%2==0).collect(Collectors.toList());
            arrEvenNumbers.stream().forEach(System.out::println);
    
            System.out.println("Length of Elements");
            List<Integer> arrEvenNumbers2 = arrNames.stream().map(str->str.length()).collect(Collectors.toList());
            arrEvenNumbers2.stream().forEach(System.out::println);
        }
    

    Output

    Collect Even Nos in list
    2
    4
    6
    Length of Elements
    4
    6
    5
    3
    
  10. Find the Sum of Nos in List?
    
    
  11. Find the Sum of Nos in List?
    
    
  12. Find the Sum of Nos in List?
    
    

When to use Lambda Expressions
If you have a functional Interface which has only one Abstract Method then we can use Lambda Expression at the Class which implements the functional interface
and provides the method definition for the abstract method in Functional Interface.

  1. We have a Function Interface Printable with abstract method print
  2. Two classes implements functional Interface – DailyReport and MothlyReport. They provide the method definition for Print() method in Printable Interface
  3. GenerateReport class has a downloadReport method which takes the Printable Type as argument
  4. DownloadReport Class we use the Printable reference and call the exact implementation by passing as argument

Printable.java

@FunctionalInterface
public interface Printable {
    public void print();
}

DailyReport.java

public class DailyReport implements Printable {
    @Override
    public void print() {
        System.out.println("Printing Report in Daily Format");
    }
}

MonthlyReport.java

public class MonthlyReport implements Printable {
    @Override
    public void print() {
        System.out.println("Printing Report in Monthly Format");
    }
}

GenerateReport.java

public class GenerateReport {
    public static void downloadReport(Printable printable){
        printable.print();
    }
}

Without Lambda Expression
DownloadReport.java

public class DownloadReport {
    public static void main(String[] args) {
        GenerateReport objGenReport = new GenerateReport();
        Printable objDailyReport = new DailyReport();
        Printable objMonthlyReport = new MonthlyReport();

        objGenReport.downloadReport(objDailyReport);
        objGenReport.downloadReport(objMonthlyReport);
    }
}

Output

Printing Report in Daily Format
Printing Report in Monthly Format

With Lambda Expression
Using Lambda expression we can pass method definition directly as parameter instead of using a class to extend interface and writing a method definition for the interface method
DownloadReport.java

public class DownloadReport {
    public static void main(String[] args) {
        GenerateReport objGenReport = new GenerateReport();
        objGenReport.downloadReport(() -> System.out.println("Printing Report in Daily Format using Lambda Expr"));

        Printable mnthlyReport = () -> System.out.println("Printing Report in Monthly Format using Lambda Expr");
        objGenReport.downloadReport(mnthlyReport);     
    }
}

Output

Printing Report in Daily Format using Lambda Expr
Printing Report in Monthly Format using Lambda Expr
With Lambda Expression Without Lambda Expression
We pass the method definition as Argument
  1. Method Definition in MonthlyReport.java
  2. Method Definition in DailyReport.java
  3. Object creation for DailyReport and MonthlyReport

Let’s take the below Example where we find the square of even numbers

ReduceEgs.java

public class ReduceEgs {
    public static void main(String[] args) {
        List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);

        arrNumbers.stream()
                  .filter(x-> x%2==0)
                  .map(x->x*x)
                  .forEach(System.out::println);
    }
}

In the above code we have

  1. Filter which takes Predicate as Param
  2. Map which takes Function as Param
  3. Sysout which takes Consumer as Param

The filter, Consumer and sysout have been expanded from the above code as below.

public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);
    
            arrNumbers.stream()
                      .filter(getIntegerPredicate())
                      .map(getFunction())
                      .forEach(getPrintln());
        }
    
        private static Consumer<Integer> getPrintln() {
            return System.out::println;
        }
    
        private static Function<Integer, Integer> getFunction() {
            return x -> x * x;
        }
    
        private static Predicate<Integer> getIntegerPredicate() {
            return x -> x % 2 == 0;
        }
}

The Above code is refactored as below

public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);

            Function<Integer, Integer> getSquare = x -> x * x;
            Predicate<Integer>         getEvenNo = x -> x % 2 == 0;
            Consumer<Integer>          showNos   = System.out::println;

            arrNumbers.stream()
                      .filter(getEvenNo)
                      .map(getSquare)
                      .forEach(showNos);
        }
}

What the java compiler does internally changes the above code as below. It created an anonymous inner class for implementing the methods in the interface.

  1. Function has apply method which should be implemented
  2. Consumer has accept method which should be implemented
  3. Predicate has test method which should be implemented
public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);

            Function<Integer, Integer> getSquare = new Function<Integer, Integer>() {
                @Override
                public Integer apply(Integer integer) {
                    return integer * integer;
                }
            };

            Predicate<Integer> getEvenNo =  new Predicate<Integer>() {
                @Override
                public boolean test(Integer integer) {
                    return  integer % 2 == 0;
                }
            };

            Consumer<Integer> showNos = new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
                }
            };

            arrNumbers.stream()
                      .filter(getEvenNo)
                      .map(getSquare)
                      .forEach(showNos);
        }
}

The output of all the above code is one and same as below

4
16
36

We have a Employee Object with following fields – empId,salary,empAge,totalExp,empName,location,pincode. Now we are going to do following operations in the List containing Employee Object.

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class MatchEgs {
    public static void main(String[] args) {
        Employee objEmp1 = new Employee(101, "Mugil", 30, "Chennai", "600018", 5, 5000);
        Employee objEmp2 = new Employee(102, "Mani", 33, "Chennai", "600028", 4, 6000);
        Employee objEmp3 = new Employee(103, "Madhu", 32, "Chennai", "600054", 6, 10000);
        Employee objEmp4 = new Employee(104, "Vinu", 29, "Bangalore", "500234", 5, 15000);
        Employee objEmp5 = new Employee(105, "Srini", 40, "Delhi", "622142", 10, 7000);
        Employee objEmp6 = new Employee(106, "Dimple", 35, "Delhi", "622142", 5, 8000);

        List<Employee> arrEmployee = Arrays.asList(objEmp1, objEmp2, objEmp3, objEmp4, objEmp5, objEmp6);
 }
}

Check if people match the criteria by passing predicate to anyMatch, noneMatch and allMatch

        Predicate<Employee> empAgeGreaterThan35 = employee -> employee.getEmpAge() > 35;
        Predicate<Employee> empAgeGreaterThan30 = employee -> employee.getEmpAge() > 30;
        Predicate<Employee> empStayingInDelhi   = employee -> employee.getLocation().equals("Delhi");

        //Check if Some Employee Age is Greater 30
        System.out.println(arrEmployee.stream().anyMatch(empAgeGreaterThan35));

        //Check if all Employee Age is above 30
        System.out.println(arrEmployee.stream().allMatch(empAgeGreaterThan30));

        //Check if any Employee Location in Delhi
        System.out.println(arrEmployee.stream().noneMatch(empStayingInDelhi));

Sort Employee by Name passing Comparator using Sorted and reversed. Sort based on more than
one field using thenComparing

        Comparator<Employee> sortByName = Comparator.comparing(Employee::getEmpName);
        List<Employee> arrSortedByName = arrEmployee.stream()
                                                    .sorted(sortByName)
                                                    .collect(Collectors.toList());
        System.out.println("----------------Sorted By Name----------------");
        arrSortedByName.forEach(employee -> System.out.println(employee));

        Comparator<Employee> sortByNameReversed = Comparator.comparing(Employee::getEmpName).reversed();
        List<Employee> arrSortedByNameRev = arrEmployee.stream()
                                                       .sorted(sortByNameReversed)
                                                       .collect(Collectors.toList());
        System.out.println("----------------Sorted By Name Reversed----------------");
        arrSortedByNameRev.forEach(employee -> System.out.println(employee));

        Comparator<Employee> sortByNameanAge = Comparator.comparing(Employee::getEmpName).thenComparing(Employee::getEmpAge);
        List<Employee> arrSortedByNameAge = arrEmployee.stream()
                                                       .sorted(sortByNameanAge)
                                                       .collect(Collectors.toList());
        System.out.println("----------------Sorted By Name and Age----------------");
        arrSortedByNameAge.forEach(employee -> System.out.println(employee));

Limit and Skip records using limit and skip

        System.out.println("----------------Limit Records to 3 ----------------");
        List<Employee> arrEmp3 =  arrEmployee.stream()
                                             .limit(3)
                                             .collect(Collectors.toList());
        arrEmp3.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Skip First 3 Records ----------------");
        List<Employee> arrEmp4 =  arrEmployee.stream()
                                             .skip(3)
                                             .collect(Collectors.toList());
        arrEmp4.forEach(employee -> System.out.println(employee));

doWhile when condition is true using takeWhile and vice-versa(Until condition is met) using dropWhile

        System.out.println("----------------Print Records until Chennai - Keeps printing until condition is True----------------");
        List<Employee> arrEmp5 =  arrEmployee.stream()
                                             .takeWhile(employee -> employee.getLocation().equals("Chennai"))
                                             .collect(Collectors.toList());
        arrEmp5.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Print Records until Chennai - Stops printing when condition is Met ----------------");
        List<Employee> arrEmp6 =  arrEmployee.stream()
                                             .dropWhile(employee -> employee.getLocation().equals("Bangalore"))
                                             .collect(Collectors.toList());
        arrEmp5.forEach(employee -> System.out.println(employee));

Get Minimum and Maximum age of employee using min and max. The List should be sorted first using comparator. Similarly get Employee with max experience in a particular location using predicate, comparator and max.

        System.out.println("----------------Record with Min and Max Value ----------------");
        Comparator<Employee> sortByAge = Comparator.comparing(Employee::getEmpAge);
        List<Employee> arrEmp7 =  arrEmployee.stream()
                                             .min(sortByAge)
                                             .stream()
                                             .collect(Collectors.toList());
        List<Employee> arrEmp8 =  arrEmployee.stream()
                                             .max(sortByAge)
                                             .stream()
                                             .collect(Collectors.toList());
        arrEmp7.forEach(employee -> System.out.println(employee));
        arrEmp8.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Get Employee with Max Exp in Chennai ----------------");
        Predicate<Employee> filterEmpInChennai = employee -> employee.getLocation().equals("Chennai");
        Comparator<Employee> sortByExp = Comparator.comparing(Employee::getTotalExp);
        List<Employee> arrEmp11 =  arrEmployee.stream()
                                              .filter(filterEmpInChennai)
                                              .max(sortByExp)
                                              .stream()
                                              .collect(Collectors.toList());
        arrEmp11.forEach(employee -> System.out.println(employee));

FindFirst Employee who matches criteria and FindAny who matches criteria. Both takes predicate as input.

findAny – Returns an Optional describing some element of the stream, or an empty Optional if the stream is empty.it is free to select any element in the stream. This is to allow for maximal performance in parallel operations;

        System.out.println("----------------Find First  ----------------");
        Predicate<Employee> empStayingInChennai = employee -> employee.getLocation().equals("Chennai");
        List<Employee> arrEmp9 =  arrEmployee.stream()
                                             .filter(empStayingInChennai)
                                             .findFirst()
                                             .stream()
                                             .collect(Collectors.toList());

        arrEmp9.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Find Any  ----------------");
        List<Employee> arrEmp10 =  arrEmployee.stream()
                                              .filter(empAgeGreaterThan30)
                                              .findAny()
                                              .stream()
                                              .collect(Collectors.toList());
        arrEmp10.forEach(employee -> System.out.println(employee));

Get the Sum of salary of Employees in Location(Chennai) using sum

         System.out.println("----------------Sum - Get Sum of Salary in Chennai  ----------------");
        //Method 1
        Integer empTotalSalaryInChennai1 = arrEmployee.stream()
                                                      .filter(empStayingInChennai)
                                                      .map(employee -> employee.getSalary())
                                                      .collect(Collectors.toList())
                                                      .stream()
                                                      .reduce(0, Integer::sum);
        System.out.println("Sum of Empl Salary - Chennai " + empTotalSalaryInChennai1);

        //Method 2
        Integer empTotalSalaryInChennai2 = arrEmployee.stream()
                                                      .filter(empStayingInChennai)
                                                      .mapToInt(Employee::getSalary).sum();
        System.out.println("Sum of Empl Salary - Chennai " + empTotalSalaryInChennai2);

        //Method 3
        Integer empTotalSalaryInChennai3 = arrEmployee.stream() 
                                                      .filter(empStayingInChennai)
                                                      .map(employee -> employee.getSalary())
                                                      .collect(Collectors.summingInt(Integer::intValue));
        System.out.println("Sum of Empl Salary - Chennai " + empTotalSalaryInChennai3);

Get Max and Min salary of employee

  Comparator<Employee> cmpSalComp = (Employee objEmpp1, Employee objEmpp2) -> Double.compare(objEmpp1.Salary,objEmpp2.Salary);

  Employee empMaxObj = arrEmpl.stream()
                                   .max(cmpSalComp)
                                   .get();
                                   
  Employee empMinObj =   arrEmpl.stream()
                                 .min(cmpSalComp)
                                 .get();

Get Average salary of employee grouped by Location

        System.out.println("----------------Average - Grouped by Location  ----------------");
        Map<String, Double> arrAvgSalByLoc =  arrEmpl.stream()
                                                     .collect(Collectors.groupingBy(Employee::getLocation, Collectors.averagingDouble(Employee::getSalary)));
                                         
        System.out.print(arrAvgSalByLoc);

Get Average salary of employee in location using average

        System.out.println("----------------Average - Get Average of Salary in Chennai  ----------------");
        OptionalDouble empAvgSalaryInChennai = arrEmployee.stream()
                                                          .filter(empStayingInChennai)
                                                          .mapToInt(Employee::getSalary)
                                                          .average();
        System.out.println("Average Salary of Employee - Chennai " + empAvgSalaryInChennai);

Get List of Employees in a location using groupingBy. The Return type is hashmap with location as key and List of employees in value

        System.out.println("----------------Group By -  Get Employees grouped by Location  ----------------");
        Map<String, List<Employee>> hmEmp13 = arrEmployee.stream()
                                                         .collect(Collectors.groupingBy(Employee::getLocation));
        hmEmp13.forEach((s, employees) -> {
            System.out.println("Employees from "+ s);
            employees.forEach(employee -> System.out.println(employee));
        });

Get Employee grouped by Location and getting Maximum Salary groupingBy and maxBy

        System.out.println("----------------Group By -  Get Employees with Max Salary in Location  ----------------");
        Comparator<Employee> cmprSalary = Comparator.comparing(Employee::getSalary);
        Map<String, Optional<Employee>> hmEmp14 = arrEmployee.stream()
                                                             .collect(Collectors.groupingBy(Employee::getLocation, Collectors.maxBy(cmprSalary)));

        hmEmp14.forEach((s, employee) -> {
            System.out.print("Employee Location is "+ s +  " and salary is ");
            employee.ifPresent(emp -> System.out.println(emp.getSalary()));
        });

Get Employee Name using mapping grouped by Location using groupingBy

        System.out.println("---------------- Employee at Location -  Using Collectors.mapping  ----------------");
        Map<String, List<String>> hmEmp16 = arrEmployee.stream()
                                                       .collect(Collectors.groupingBy(Employee::getLocation, Collectors.mapping(Employee::getEmpName, Collectors.toList())));

        hmEmp16.forEach((s, employee) -> {
            System.out.println("Employee Name is " + employee + " and Location is "+ s );

        });

Output

true
false
false
----------------Sorted By Name----------------
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
----------------Sorted By Name Reversed----------------
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
----------------Sorted By Name and Age----------------
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
----------------Limit Records to 3 ----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Skip First 3 Records ----------------
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
----------------Print Records until Chennai - Keeps printing until condition is True----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Print Records until Chennai - Stops printing when condition is Met ----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Record with Min and Max Value ----------------
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
----------------Get Employee with Max Exp in Chennai ----------------
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Find First  ----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
----------------Find Any  ----------------
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
----------------Sum - Get Sum of Salary in Chennai  ----------------
Sum of Empl Salary - Chennai 21000
Sum of Empl Salary - Chennai 21000
Sum of Empl Salary - Chennai 21000
----------------Average - Get Average of Salary in Chennai  ----------------
Average Salary of Employee - Chennai OptionalDouble[7000.0]
----------------Group By -  Get Employees grouped by Location  ----------------
Employees from Delhi
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
Employees from Chennai
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employees from Bangalore
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
----------------Group By -  Get Employees with Max Salary in Location  ----------------
Employee Location is Delhi and salary is 8000
Employee Location is Chennai and salary is 10000
Employee Location is Bangalore and salary is 15000
---------------- Employee at Location -  Get Employee Object grouped by Location  ----------------
Employee Location is Delhi
Srini
Dimple
Employee Location is Chennai
Mugil
Mani
Madhu
Employee Location is Bangalore
Vinu
---------------- Employee at Location -  Using Collectors.mapping  ----------------
Employee Name is [Srini, Dimple] and Location is Delhi
Employee Name is [Mugil, Mani, Madhu] and Location is Chennai
Employee Name is [Vinu] and Location is Bangalore

How to find the Stream is Primitive or Wrapper?

public class PrimStreams {
    public static void main(String[] args) {
        List<Integer> arrReviews1 = List.of(8, 9, 8 , 6, 2, 7, 6);
        System.out.println(arrReviews1.stream());

        int[] arrReviews2 = {8, 9, 8 , 6, 2, 7, 6};
        System.out.println(Arrays.stream(arrReviews2));

        System.out.println("----------Wrapper Streams Starts----------");
        System.out.println(arrReviews1.stream().reduce(Integer::sum));
        System.out.println(arrReviews1.stream().reduce(Integer::min));
        System.out.println(arrReviews1.stream().reduce(Integer::max));
        System.out.println(arrReviews1.stream().mapToInt(Integer::intValue).average().orElse(Double.NaN));

        System.out.println("----------Primitive Streams Starts----------");
        System.out.println(Arrays.stream(arrReviews2).sum());
        System.out.println(Arrays.stream(arrReviews2).min());
        System.out.println(Arrays.stream(arrReviews2).max());
        System.out.println(Arrays.stream(arrReviews2).average());
    }
}

In the below output you may see the wrapper class stream would be printed as ReferencePipeline whereas primitive stream would be displayed as IntPipeline. The above code contains doing some basic operation in both primitive and wrapper stream. If the stream is primitive type it would be easy to carry out operation over stream and more memory efficient since there would be no boxing and unboxing involved
Output

java.util.stream.ReferencePipeline$Head@1d251891
java.util.stream.IntPipeline$Head@2133c8f8
----------Wrapper Streams Starts----------
Optional[46]
Optional[2]
Optional[9]
6.571428571428571
----------Primitive Streams Starts----------
46
OptionalInt[2]
OptionalInt[9]
OptionalDouble[6.571428571428571]

How to create a primitive stream of Int?

  IntStream arrReviews3 = IntStream.of(8, 9, 8 , 6, 2, 7, 6);

Basic Operations in primitive stream of Int?

  1. range – Considers nos in specific range.range doesnot takes upper limit no.To address that rangeClosed is used
  2. iterate– Iterate helps in iterating till a particular range by taking Initial and urnaryOperator as params. The default behaviour of Iterator is endless loop.To stop after particular limit like one we did in range we should use limit function.
  3. peek– peek helps going through the values in stream without making any changes. This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
  4. boxed– boxed will help in boxing primitive to wrapper while collecting the values in to a list.
IntStream arrReviews = IntStream.of(8, 9, 8 , 6, 2, 7, 6);

        System.out.println("----------Stream printing Range of no ----------");

        System.out.println("range doesnot include last no 10 so sum is from 1 to 9 - " + IntStream.range(0,10).sum());
        System.out.println("rangeClosed would include last no 1 to 10 - " + IntStream.rangeClosed(0,10).sum());

        System.out.println("Using Peek we can look at nos in stream");
        System.out.println(IntStream.rangeClosed(0,10)
                                    .peek(System.out::println)
                                    .sum());

        System.out.println("iterate helps in iterating till a specific value");
        System.out.println(IntStream.iterate(1,e->e+1)
                                    .peek(System.out::println)
                                    .limit(10)
                                    .sum());

        System.out.println("iterate helps in iterating till a specific value");
        System.out.println(IntStream.iterate(1,e->e+2)
                                    .peek(System.out::println)
                                    .limit(10)
                                    .sum());

        System.out.println("Iterating over range and collecting in a list");
        List<Integer> arrIntegStream = IntStream.iterate(1,e->e+1)
                                                .peek(System.out::println)
                                                .limit(10)
                                                .boxed()
                                                .collect(Collectors.toList());
        System.out.println(arrIntegStream);

Finding Factorial of No of range 5

public class Factorial {
    public static void main(String[] args) {
        System.out.println(IntStream.rangeClosed(1,5).reduce(1, (x,y)->x*y));

        int i,fact=1;
        int number=5;//It is the number to calculate factorial
        for(i=1;i<=number;i++){
            fact=fact*i;
        }
        System.out.println(fact);
    }
}

Find Maximum of Number in Stream

class HelloWorld {
    public static void main(String[] args) {
       List<Integer> arrNum  = List.of(3,4,5,2,1);
       
       Integer max = arrNum.stream()
                           .max(Integer::compare)
                           .get();
                            
       System.out.print(max);
    }
}

Output

5

The same when used in Objects comparator should be used as one below.

Find Maximum of Number in Stream

.
.       
      Comparator<Employee> sortByExp = Comparator.comparing(Employee::getTotalExp);
      List<Employee> arrEmp11 =  arrEmployee.stream()      
                                            .max(sortByExp);
.
.

Basic String Operation in Streams

  1. Join strings in a list using Collectors.joining
  2. Make Array of String using split. Split would return Array Stream
  3. Apply transformation in elements in list using replaceAll
  4. To flatten the Array Stream in the List use flatMap
  5. Get rid of particular element in List of String using removeIf
  6. Use replaceAll to carry Out Transformation similar to map()
  7. Use peek to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StringOps {
    public static void main(String[] args) {
        List<String> arrSkills = List.of("Core Java", "Core Spring", "Spring MVC", "Spring Security", "Spring Boot", "RestAPI");

        System.out.println("-----Join Elements in List using Seperator - [,]---------");
        String skills = arrSkills.stream().collect(Collectors.joining(","));
        System.out.println("Skills are "+ skills);

        System.out.println("-----Split String and Store in String Array---------");
        String[] arrSkillList = skills.split(",");
        Arrays.stream(arrSkillList).forEach(System.out::println);

        System.out.println("-----Convert to UpperCase---------");
        List<String> arrModSkills = new ArrayList(arrSkills);
        arrModSkills.replaceAll(str -> str.toUpperCase());
        arrModSkills.forEach(System.out::println);

        System.out.println("-----Using flatMap to flatten Arrays of Stream and Getting Distinct Location---------");
        List<String> arrLocation = List.of("Madras,Mumbai,Bangalore", "Madras,Kolkatta,Delhi", "Delhi,Madras");
        List allLocations = arrLocation.stream()
                                       .map(location->location.split(","))
                                       .peek(loc-> System.out.println(loc))
                                       .flatMap(Arrays::stream)
                                       .distinct()
                                       .collect(Collectors.toList());

        allLocations.stream()
                    .map(loc->replaceMadras(loc.toString()))
                    .forEach(System.out::println);

        System.out.println("------------Remove Particular Location using removeIf------------");
        allLocations.removeIf(loc->loc.equals("Delhi"));
        allLocations.forEach(loc-> System.out.println(loc));
    }

    private static String replaceMadras(String location){
        if(location.equals("Madras"))
            return "Chennai";
        else
            return location;
    }
}

Converting Multiple List into Single List

public class StreamMisc {
    public static void main(String[] args) {
        List<List<String>> arrSkills = new ArrayList<>();
        arrSkills.add(Arrays.asList("Java", "C++", "C#"));
        arrSkills.add(Arrays.asList("HTML", "CSS"));
        arrSkills.add(Arrays.asList("SQL", "MySQL"));
        arrSkills.add(Arrays.asList("Azure"));

        List<String> arrAllSkills = arrSkills.stream().reduce(new ArrayList<>(), (a, b) -> {
            a.addAll(b);
            return a;
        });

        arrAllSkills.forEach((skill) -> System.out.print(skill + ","));
    }
}

Output

Java,C++,C#,HTML,CSS,SQL,MySQL,Azure

Higher-order functions are functions that take other functions as arguments or return functions as their results.

  1. In the below example we Search employee by Location using Predicate.
  2. If we are not using HOF we need to assign new predicate to variable each and everytime like empFromDelhi
  3. Instead of doing that we have a HOF getEmpByLocation which takes location as string in arguments and returns function in its return type
public class HigherOrdFns {
    public static void main(String[] args) {
        Employee objEmp1 = new Employee(101, "Mugil", 30, "Chennai", "600018", 5, 5000);
        Employee objEmp2 = new Employee(102, "Mani", 33, "Chennai", "600028", 4, 6000);
        Employee objEmp3 = new Employee(103, "Madhu", 32, "Chennai", "600054", 6, 10000);
        Employee objEmp4 = new Employee(104, "Vinu", 29, "Bangalore", "500234", 5, 15000);
        Employee objEmp5 = new Employee(105, "Srini", 40, "Delhi", "622142", 10, 7000);
        Employee objEmp6 = new Employee(106, "Dimple", 35, "Delhi", "622142", 5, 8000);

        List<Employee> arrEmployee = Arrays.asList(objEmp1, objEmp2, objEmp3, objEmp4, objEmp5, objEmp6);

        Predicate<Employee> empFromDelhi = employee -> employee.getLocation().equals("Delhi");

        arrEmployee.stream().filter(empFromDelhi).forEach(System.out::println);
        arrEmployee.stream().filter(getEmpByLocation("Chennai")).forEach(System.out::println);
        arrEmployee.stream().filter(getEmpByLocation("Bangalore")).forEach(System.out::println);
    }

    //Higher Order function which return function as return type
    private static Predicate<Employee> getEmpByLocation(String location) {
        return employee -> employee.getLocation().equals(location);
    }
}
  1. forEach method in java.lang.Iterable interface

    Java 8 has introduced forEach method in java.lang.Iterable interface so that while writing code we focus on business logic. The forEach method takes java.util.function.Consumer object as an argument, so it helps in having our business logic at a separate location that we can reuse.

  2. myList.forEach(new Consumer < Integer > () {
        public void accept(Integer t) {
            System.out.println("forEach anonymous class Value::" + t);
        }
    
    });
    
    //traversing with Consumer interface implementation
    MyConsumer action = new MyConsumer();
    myList.forEach(action);
    

    Consumer implementation that can be reused

    class MyConsumer implements Consumer<Integer>{
    
    	public void accept(Integer t) {
    		System.out.println("Consumer impl Value::"+t);
    	}
    }
    
  3. Interfaces with default and static methods

    From Java 8, interfaces are enhanced to have a method with implementation. We can use default and static keyword to create interfaces with method implementation

  4. We know that Java doesn’t provide multiple inheritance in Classes because it leads to Diamond Problem. So how it will be handled with interfaces now since interfaces are now similar to abstract classes. Compiler will throw an exception in this scenario and we will have to provide implementation logic in the class implementing the interfaces.

    @FunctionalInterface
    public interface Interface1 {
    
        void method1(String str);
    
        default void log(String str) {
            System.out.println("I1 logging::" + str);
        }
    
        static void print(String str) {
            System.out.println("Printing " + str);
        }
    }    
    

    interfaces are not allowed to have Object default methods.

  5. @FunctionalInterface annotation. Functional interfaces are a new concept introduced in Java 8. An interface with exactly one abstract method becomes a Functional Interface. We don’t need to use @FunctionalInterface annotation to mark an interface as a Functional Interface.

    @FunctionalInterface annotation is a facility to avoid the accidental addition of abstract methods in the functional interfaces. You can think of it like @Override annotation and it’s best practice to use it. java.lang.Runnable with a single abstract method run() is a great example of a functional interface.

    Anonymous Class Implementation

    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("My Runnable");
        }
    };
    

    Lambda Expression Implementation

    Runnable r1 = () - > {
        System.out.println("My Runnable");
    };
    

    If you have single statement in method implementation anonymous class can be instantiated using lambda expression as below
    lambda expressions Implementation

    Interface1 i1 = (s) -> System.out.println(s);		
    i1.method1("abc");
    
  6. New java.util.stream has been added in Java 8 to perform filter/map/reduce like operations with the collection. Stream API will allow sequential as well as parallel execution.
    public static void main(String[] args) {
    
        List < Integer > myList = new ArrayList < > ();
        for (int i = 0; i < 100; i++) myList.add(i);
    
        //sequential stream
        Stream < Integer > sequentialStream = myList.stream();
    
        //parallel stream
        Stream < Integer > parallelStream = myList.parallelStream();
    
        //using lambda with Stream API, filter example
        Stream < Integer > highNums = parallelStream.filter(p - > p > 90);
        //using lambda in forEach
        highNums.forEach(p - > System.out.println("High Nums parallel=" + p));
    
        Stream < Integer > highNumsSeq = sequentialStream.filter(p - > p > 90);
        highNumsSeq.forEach(p - > System.out.println("High Nums sequential=" + p));
    
    }
    

    parallel processing values are not in order, so parallel processing will be very helpful while working with huge collections.

  7. IO improvements known to me are:

    Files.list(Path dir) that returns a lazily populated Stream, the elements of which are the entries in the directory.
    Files.lines(Path path) that reads all lines from a file as a Stream.
    Files.find() that returns a Stream that is lazily populated with Path by searching for files in a file tree rooted at a given starting file.
    BufferedReader.lines() that return a Stream, the elements of which are lines read from this BufferedReader.

  8. Java Time API packages, I can sense that they will be very easy to use. It has some sub-packages java.time.format that provides classes to print and parse dates and times and java.time.zone provides support for time zones and their rules.

How to Search for Product with least cost from List? I have a List of Speakers in List. I want to get the speaker with the least cost.

Speakers.java

package com.mugil.bean;

public class Speakers {
 private Integer speakerID;
 private String companyName;
 private Integer price;
 private String sellerName;

 public Speakers(Integer pSpeakerId, String pcompanyName, Integer pPrice, String psellerName) {
  this.speakerID = pSpeakerId;
  this.companyName = pcompanyName;
  this.price = pPrice;
  this.sellerName = psellerName;
 }

 public int getSpeakerID() {
  return speakerID;
 }
 public void setSpeakerID(int speakerID) {
  this.speakerID = speakerID;
 }
 public String getCompanyName() {
  return companyName;
 }
 public void setCompanyName(String companyName) {
  this.companyName = companyName;
 }
 public int getPrice() {
  return price;
 }
 public void setPrice(int price) {
  this.price = price;
 }

 public String getSellerName() {
  return sellerName;
 }

 public void setSellerName(String sellerName) {
  this.sellerName = sellerName;
 }

 @Override
 public String toString() {
  return "Speakers [speakerID=" + speakerID + ", companyName=" + companyName + ", price=" + price +
   ", sellerName=" + sellerName + "]";
 }
}

SearchSpeakers.java

package com.mugil.test;

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

import com.mugil.bean.Speakers;

public class SearchSpeakers {

 public static void main(String[] args) {
  Speakers objSpeaker1 = new Speakers(101, "JBL Go", 120, "Amazon");
  Speakers objSpeaker2 = new Speakers(102, "Panasonic", 420, "Amazon");
  Speakers objSpeaker3 = new Speakers(103, "JBL Go", 900, "Flipkart");
  Speakers objSpeaker4 = new Speakers(104, "JBL Go", 120, "Amazon");
  Speakers objSpeaker5 = new Speakers(105, "JBL Go", 350, "Amazon");
  Speakers objSpeaker6 = new Speakers(101, "Philips", 120, "EBay");
  Speakers objSpeaker7 = new Speakers(102, "JBL Go", 125, "Ebay");
  Speakers objSpeaker8 = new Speakers(103, "JBL Go", 35, "Smart Shoppe");
  Speakers objSpeaker9 = new Speakers(104, "Panasonic", 80, "Amazon");
  Speakers objSpeaker10 = new Speakers(105, "Philips", 180, "Amazon");

  List < Speakers > arrSpeakers = new ArrayList < Speakers > ();

  arrSpeakers.add(objSpeaker1);
  arrSpeakers.add(objSpeaker2);
  arrSpeakers.add(objSpeaker3);
  arrSpeakers.add(objSpeaker4);
  arrSpeakers.add(objSpeaker5);
  arrSpeakers.add(objSpeaker6);
  arrSpeakers.add(objSpeaker7);
  arrSpeakers.add(objSpeaker8);
  arrSpeakers.add(objSpeaker9);
  arrSpeakers.add(objSpeaker10);

  Comparator < Speakers > spkComparator = (Speakers objSpeaker1a, Speakers objSpeaker2a) -> objSpeaker1a.getPrice() - objSpeaker2a.getPrice();

  arrSpeakers.stream().sorted(spkComparator).collect(Collectors.groupingBy(Speakers::getCompanyName)).values()
   .stream().forEach(searchedModel -> System.out
    .println("Speaker with Lowest Cost" + searchedModel.stream().findFirst()));
 }
}
arrSpeakers.stream().sorted(spkComparator).collect(Collectors.groupingBy(Speakers::getCompanyName)).values()
   .stream().forEach(searchedModel -> System.out
    .println("Speaker with Lowest Cost" + searchedModel.stream().findFirst()));

arrSpeakers.stream() – Convert to Stream
arrSpeakers.stream().sorted(spkComparator) – Sorts the list by price
arrSpeakers.stream().sorted(spkComparator).collect(Collectors.groupingBy(Speakers::getCompanyName)) – Groups the Sorted List by Company Name
arrSpeakers.stream().sorted(spkComparator).collect(Collectors.groupingBy(Speakers::getCompanyName)).values()
.stream()
– Convert the Sorted List into Stream

The above code could be rewritten as follows

//Sort by Price and group by company name
Stream objStream = arrSpeakers.stream()
                              .sorted(spkComparator)
                              .collect(Collectors.groupingBy(Speakers::getCompanyName)).values();

//Loop through Stream of List and print the first one
objStream.forEach(listOfStudent -> System.out.println((ArrayList)listOfStudent.stream().findFirst()));

Output

Speaker with Lowest CostOptional[Speakers [speakerID=101, companyName=Philips, price=120, sellerName=EBay]]
Speaker with Lowest CostOptional[Speakers [speakerID=104, companyName=Panasonic, price=80, sellerName=Amazon]]
Speaker with Lowest CostOptional[Speakers [speakerID=103, companyName=JBL Go, price=35, sellerName=Smart Shoppe]]

Type inference is a feature of Java which provides ability to compiler to look at each method invocation and corresponding declaration to determine the type of arguments.
Java provides improved version of type inference in Java 8.
Here, we are creating arraylist by mentioning integer type explicitly at both side. The following approach is used earlier versions of Java.

List<Integer> list = new ArrayList<Integer>();  

In the following declaration, we are mentioning type of arraylist at one side. This approach was introduce in Java 7. Here, you can left second side as blank diamond and compiler will infer type of it by type of reference variable.

List<Integer> list2 = new ArrayList<>();   

Improved Type Inference
In Java 8, you can call specialized method without explicitly mentioning of type of arguments.

showList(new ArrayList<>());  

Example
You can use type inference with generic classes and methods.

import java.util.ArrayList;
import java.util.List;
public class TypeInferenceExample {
 public static void showList(List < Integer > list) {
  if (!list.isEmpty()) {
   list.forEach(System.out::println);
  } else System.out.println("list is empty");
 }

 public static void main(String[] args) {

  // An old approach(prior to Java 7) to create a list  
  List < Integer > list1 = new ArrayList < Integer > ();
  list1.add(11);
  showList(list1);

  // Java 7    
  List < Integer > list2 = new ArrayList < > (); // You can left it blank, compiler can infer type  
  list2.add(12);
  showList(list2);

  // Compiler infers type of ArrayList, in Java 8  
  showList(new ArrayList < > ());
 }
}

Output

11
12
list is empty

Type inference for Custom Classes

class GenericClass <X> {
 X name;
 public void setName(X name) {
  this.name = name;
 }
 public X getName() {
  return name;
 }
 public String genericMethod(GenericClass < String > x) {
  x.setName("John");
  returnx.name;
 }
}

public class TypeInferenceExample {
 public static void main(String[] args) {
  GenericClass < String > genericClass = new GenericClass < String > ();
  genericClass.setName("Peter");
  System.out.println(genericClass.getName());

  GenericClass < String > genericClass2 = new GenericClass < > ();
  genericClass2.setName("peter");
  System.out.println(genericClass2.getName());

  // New improved type inference  
  System.out.println(genericClass2.genericMethod(new GenericClass < > ()));
 }
}

Output

Peter
peter
John

Lambdas implement a functional interface.Anonymous Inner Classes can extend a class or implement an interface with any number of methods.
Variables – Lambdas can only access final or effectively final.
State – Anonymous inner classes can use instance variables and thus can have state, lambdas cannot.
Scope – Lambdas can’t define a variable with the same name as a variable in enclosing scope.
Compilation – Anonymous compiles to a class, while lambda is an invokedynamic instruction.

Syntax
Lambda expressions looks neat as compared to Anonymous Inner Class (AIC)

public static void main(String[] args) {
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("in run");
        }
    };

    Thread t = new Thread(r);
    t.start(); 
}

//syntax of lambda expression 
public static void main(String[] args) {
    Runnable r = ()->{System.out.println("in run");};
    Thread t = new Thread(r);
    t.start();
}

Scope
An anonymous inner class is a class, which means that it has scope for variable defined inside the inner class.

Whereas,lambda expression is not a scope of its own, but is part of the enclosing scope.

Similar rule applies for super and this keyword when using inside anonymous inner class and lambda expression. In case of anonymous inner class this keyword refers to local scope and super keyword refers to the anonymous class’s super class. While in case of lambda expression this keyword refers to the object of the enclosing type and super will refer to the enclosing class’s super class.

//AIC
    public static void main(String[] args) {
        final int cnt = 0; 
        Runnable r = new Runnable() {
            @Override
            public void run() {
                int cnt = 5;    
                System.out.println("in run" + cnt);
            }
        };

        Thread t = new Thread(r);
        t.start();
    }

//Lambda
    public static void main(String[] args) {
        final int cnt = 0; 
        Runnable r = ()->{
            int cnt = 5; //compilation error
            System.out.println("in run"+cnt);};
        Thread t = new Thread(r);
        t.start();
    }

Performance
At runtime anonymous inner classes require class loading, memory allocation, and object initialization and invocation of a non-static method while lambda expression is a compile-time activity and don’t incur extra cost during runtime. So the performance of lambda expression is better as compare to anonymous inner classes.