Google

Apr 9, 2014

Understanding Java 8 Streams and working with collections using Lambda expressions

Assumes that you have read and understand Java 8 Lambda Expressions and functional interface tutorials

A stream is an infinite sequence of consumable elements (i.e a data structure) for the consumption of an operation or iteration. Any Collection can be exposed as a stream. The operations you perform on a stream can either be
  • intermediate (map, filter, sorted, limit, skip,concat, substream, distinct, etc) producing another stream or 
  • terminal (forEach, reduce, collect, sum, max, count, matchAny, findFirst, findAny, etc) producing an object that is not a stream.
Basically, you are building a pipeline as in Unix

ls -l | grep "Dec" | Sort +4n | more


stream( ) is a default method added to the Collection interface in Java 8. The stream( ) returns a java.util.Stream interface with multiple abstract methods like filter, map, sorted, collect, etc.  DelegatingStream is implements these abstract methods.

Java 8 Example 1:


package com.java8.examples;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class EmployeeTest {
 
 private static List<Employee> employees = Arrays.asList(
  new Employee("Steve", BigDecimal.valueOf(35000), Employee.WorkType.PARTTIME), 
  new Employee("Peter", BigDecimal.valueOf(65000), Employee.WorkType.FULLTIME),
  new Employee("Sam", BigDecimal.valueOf(75000), Employee.WorkType.FULLTIME),
  new Employee("John", BigDecimal.valueOf(25000), Employee.WorkType.CASUAL));
        
 
  public static void main(String[] args) {
   
   //e is the parameter for Employee
   List<Employee> fullTimeEmployees = employees.stream()  //returns a stream (intermediate)
      .filter(e -> e.getWorkType() == Employee.WorkType.FULLTIME)//returns a stream (intermediate)
      .collect(Collectors.toList()); // returns a list (terminal)
   fullTimeEmployees.forEach(e -> System.out.println(e)); //Peter & Sam  
 }

}


The output is:

Employee [name=Peter, salary=65000, workType=FULLTIME]
Employee [name=Sam, salary=75000, workType=FULLTIME]


The above example creates a new list of full time employees. The operations .stream( ),  .filter( ) create the intermediate streams, hence they are chained, and the .collect is the terminal operation that returns the final List of full-time employees.

This is enabled in the Iterable interface, which is a functional interface with the forEach default method. A List and other collections implements the Iterable functional interface to allow lambda expressions.

The Employee class will look like

package com.java8.examples;
import java.math.BigDecimal;

public class Employee {
 
 public enum WorkType {FULLTIME, PARTTIME, CASUAL}
 
 private String name;
 private BigDecimal salary;
 private WorkType workType;
 
 public Employee(String name, BigDecimal salary, WorkType workType) {
  super();
  this.name = name;
  this.salary = salary;
  this.workType = workType;
 }
 
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public BigDecimal getSalary() {
  return salary;
 }
 public void setSalary(BigDecimal salary) {
  this.salary = salary;
 }
 public WorkType getWorkType() {
  return workType;
 }
 public void setWorkType(WorkType workType) {
  this.workType = workType;
 }

 @Override
 public String toString() {
  return "Employee [name=" + name + ", salary=" + salary + ", workType=" + workType + "]";
 }
}


Java 8 Example 2:

Now, in the same example, if you want to print all the employees who earn more than 25,000, sorted by WorkType,  in the format "<name> earns <salary>  on a <work type> basis.

//e is the parameter for Employee
employees.stream()  //returns a stream (intermediate)
 .filter(e -> e.getSalary().doubleValue() > 25000) // returns a stream (intermediate)
 .sorted((e1,e2) -> e1.getWorkType().compareTo(e2.getWorkType()))
 .map(e -> e.getName() + " earns " + e.getSalary() + " on a " + e.getWorkType() + " basis")
 .forEach(System.out::println); //terminal 
 

Isn't this cool? This is like writing the SQL where clause.

Output:

Peter earns 65000 on a FULLTIME basis
Sam earns 75000 on a FULLTIME basis
Steve earns 35000 on a PARTTIME basis


Java 8 Example 3:

Get a  comma separated  string of all employees earning more than 25,000.00 as salary.

//e is the parameter for Employee
String str = employees.stream()  //returns a stream (intermediate)
 .filter(e -> e.getSalary().doubleValue() > 25000) // returns a stream (intermediate)
 .map(e -> e.getName())// returns a stream (intermediate)
 .distinct()  //returns a stream (intermediate)
  //terminal returning a string 
 .reduce("Distinct first names earning > 25000:", (name1, name2) -> name1+ "," + name2 );
   
System.out.println("CSV: " + str);


Output:

CSV: Distinct first names earning > 25000:,Steve,Peter,Sam



Java 8 Example 4:

Aggregate the salary by WorkType.

//e is the parameter for Employee
employees.stream()  //returns a stream (intermediate)
 .collect(Collectors.groupingBy(Employee::getWorkType))
 .forEach((gr,le) -> {System.out.println(" Aggregated Salary for "  + gr + " is " +  
   + le.stream().mapToDouble(e -> e.getSalary().doubleValue()).sum());
 });


Output:

 Aggregated Salary for CASUAL is 25000.0
 Aggregated Salary for FULLTIME is 140000.0
 Aggregated Salary for PARTTIME is 35000.0


Java 8 Example 5:

Get the employee who earns the highest salary.

//e is the parameter for Employee
Employee maxSalaryEmployee = employees.stream()  //returns a stream (intermediate)
 .max((e1, e2) -> (e1.getSalary().compareTo(e2.getSalary())))
 .get();  
    
System.out.println(maxSalaryEmployee);


Output:

Employee [name=Sam, salary=75000, workType=FULLTIME]

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home