Java 8 Stream APIs

Streams are not easy to debug ūüôā

Java 8 Stream is here to ease our life as far as iteration over different Collections are concerned based on some filter conditions etc.

 

How we used to do earlier:

Say, there is one Employee Class with below properties.

public class Employee {
       private int empId;
       private String empName;
       private String gender;
       private String salary;
       // and their getters/setters
}

 

Use Case :  Filter out all Male Employees with Salary higher than 10000.

Let’s say, there is a List of Employees of size 100 with sequential empId, Gender as Male/Female and some random/same salary.

for (int count=0;count < empList.size(); count++) {
    Employee emp = empList.get(count);
    if (emp.getSalary > 10000 && "Male".equals(emp.getGender()) {
       System.out.println(emp.getEmpName());
    }
}

With for-each loop:

for (Employee emp : empList) {
    if (emp.getSalary > 10000 && "Male".equals(emp.getGender()) {
       System.out.println(emp.getEmpName());
    }
}

With Java 8 Stream API:

employees.stream().filter(emp -> emp.getSalary() > 10000)
.filter(emp -> emp.getGender().equals("Male"))
.map(Employee::getName)
.forEach(System.out::println);

And we are done.

All Stream methods:

  • boolean¬†allMatch(Predicate<? super T>¬†predicate)

Returns whether all elements of this stream match the provided predicate.

boolean isAllMale = employees.stream()
                   .allMatch(emp->emp.getGender().equals("Male"));
  • boolean¬†anyMatch(Predicate<? super T>¬†predicate)

Returns whether any elements of this stream match the provided predicate.

boolean isAnyMale = employees.stream()
                    .anyMatch(emp->emp.getGender().equals("Male"));
  • <R, A>¬†R¬†collect(Collector<? super T,A,R>¬†collector)

Performs a mutable reduction operation on the elements of this stream using a Collector

Map<String, List<Employee>> employeesByGender = employees.stream()
                 .collect(Collectors.groupingBy(Employee::getGender));
  • static¬†<T>¬†Stream<T>¬†concat(Stream<? extends T>¬†a, Stream<? extends T>¬†b)

Lets say you want to concat two different Employee Streams.

  • long count()

Returns the count of elements in this stream.

long count = employees.stream().filter(emp -> emp.getSalary() > 4000)
            .filter(emp -> emp.getGender().equals("Male")).count();

Returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.

List<Employee> emplist = employees.stream().distinct()
                         .collect(Collectors.toList());

Returns a stream consisting of the elements of this stream that match the given predicate.

List<String> empList = employees.stream()
                       .filter(emp -> emp.getGender().equals("Male"))
                       .map(Employee::getName)
                       .collect(Collectors.toList());

Returns an Optional describing some element of the stream, or an empty Optional if the stream is empty. The behavior of this operation is explicitly nondeterministic; it is free to select any element in the stream. If a stable result is desired, use findFirst() instead.

Optional op = employees.stream()
              .filter(emp -> emp.getSalary() > 3000)
              .findAny();

Returns an Optional describing the first element of this stream, or an empty Optional if the stream is empty.

Optional op = employees.stream()
              .filter(emp -> emp.getSalary() > 4000)
              .findFirst();

Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.

Imagine every Employee belongs to all department

i.e

...
private List<String> departments = 
   Arrays.asList("Hindi", "English", "History", "Geography", "Maths");
...

Then

List<String> empDeptList = employees.stream()
                      .flatMap(emp -> emp.getDepartment().stream())
                      .distinct()
                      .collect(Collectors.toList());
  • void¬†forEach(Consumer<? super T>¬†action)

Performs an action for each element of this stream.

employees.stream()
  .filter(emp -> emp.getSalary() > 4000)
  .map(Employee :: getName)
  .forEach(System.out::println);

Returns a stream consisting of the results of applying the given function to the elements of this stream

List<String> empNames = employees.stream()
                       .map(Employee :: getName)
                       .collect(Collectors.toList());

Returns the maximum element of this stream according to the provided Comparator.

employees.stream()
  .max(new Comparator<Employee>() { 
           @Override public int compare(Employee e1, Employee e2) { 
               if (e1.getSalary() > e2.getSalary()) { 
                   return 1; 
               } else if (e1.getSalary() < e2.getSalary()) { 
                   return -1; 
               } return 0; 
          } 
 }).get();

Returns the minimum element of this stream according to the provided Comparator

employees.stream()
         .min(new Comparator<Employee>() { 
              @Override public int compare(Employee e1, Employee e2) { 
                  if (e1.getSalary() > e2.getSalary()) { 
                     return 1; 
                  } else if (e1.getSalary() < e2.getSalary()) { 
                     return -1; 
                  } return 0; 
           } 
}).get();
  • static¬†<T>¬†Stream<T>¬†of(T…¬†values)

Returns a sequential ordered stream whose elements are the specified values.

Stream.of("one", "two", "three", "four")
 .filter(e -> e.length() > 3).forEach(System.out :: println);

Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.

Stream.of("one", "two", "three", "four")
 .filter(e -> e.length() > 3)
 .peek(e -> System.out.println("Filtered value: " + e))
 .collect(Collectors.toList());

Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream.

employees.stream().skip(5).forEach(System.out::println);

Returns a stream consisting of the elements of this stream, sorted according to natural order. If the elements of this stream are not Comparable, a java.lang.ClassCastException may be thrown when the terminal operation is executed.

If Employee Class is not implementing Comparable interface below code will give java.lang.ClassCastException.

employees.stream().sorted().forEach(System.out::println);

Returns a stream consisting of the elements of this stream, sorted according to the provided Comparator.

employees.stream()
.sorted(new Comparator<Employee>() { 
          @Override public int compare(Employee e1, Employee e2) { 
             if (e1.getSalary() > e2.getSalary()) { 
                 return 1; 
             } else if (e1.getSalary() < e2.getSalary()) { 
               return -1; 
             } return 0; 
         } 
 }).forEach(System.out::println);

 

GIT Repository: https://github.com/tektutorial/java-design

Hope this clears your doubt!!