Java 8 Stream API in depth
The Stream API in Java 8 is a powerful tool for processing sequences of data in a functional programming style. It allows operations like filtering, mapping, and reducing on collections and other data sources.
Here's a comprehensive, in-depth example of using the Stream API, including explanations of key methods and a real-world use case.
Scenario: Employee Management
Imagine a system that manages employees. We want to:
Filter employees by department.
Find employees earning more than a threshold salary.
Group employees by department.
Calculate the average salary of employees in each department.
Sort employees by name.
Find the employee with the highest salary.
Example Code:
import java.util.*;
import java.util.stream.*;
class Employee {
private int id;
private String name;
private String department;
private double salary;
public Employee(int id, String name, String department, double salary) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getDepartment() {
return department;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return "Employee{id=" + id + ", name='" + name + "', department='" + department + "', salary=" + salary + "}";
}
}
public class StreamAPIExample {
public static void main(String[] args) {
// Create a list of employees
List<Employee> employees = Arrays.asList(
new Employee(1, "Alice", "HR", 45000),
new Employee(2, "Bob", "Engineering", 70000),
new Employee(3, "Charlie", "Engineering", 80000),
new Employee(4, "David", "HR", 50000),
new Employee(5, "Eve", "Finance", 60000),
new Employee(6, "Frank", "Finance", 55000)
);
// 1. Filter employees by department (Engineering)
List<Employee> engineeringEmployees = employees.stream()
.filter(e -> e.getDepartment().equals("Engineering"))
.collect(Collectors.toList());
// 2. Find employees earning more than 60,000
List<Employee> highEarners = employees.stream()
.filter(e -> e.getSalary() > 60000)
.collect(Collectors.toList());
// 3. Group employees by department
Map<String, List<Employee>> employeesByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// 4. Calculate the average salary in each department
Map<String, Double> averageSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment, Collectors.averagingDouble(Employee::getSalary)));
// 5. Sort employees by name
List<Employee> sortedByName = employees.stream()
.sorted(Comparator.comparing(Employee::getName))
.collect(Collectors.toList());
// 6. Find the employee with the highest salary
Optional<Employee> highestEarner = employees.stream()
.max(Comparator.comparingDouble(Employee::getSalary));
// 7. Find distinct departments
Set<String> distinctDepartments = employees.stream()
.map(Employee::getDepartment)
.collect(Collectors.toSet());
// 8. Collect names of all employees into a list
List<String> employeeNames = employees.stream()
.map(Employee::getName)
.collect(Collectors.toList());
// 9. Concatenate all employee names into a single string
String concatenatedNames = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "));
// 10. Check if any employee earns more than 75,000
boolean anyHighEarner = employees.stream()
.anyMatch(e -> e.getSalary() > 75000);
// 11. Calculate total salary of all employees
double totalSalary = employees.stream()
.mapToDouble(Employee::getSalary)
.sum();
// Print all the results
System.out.println("Employees in Engineering:");
engineeringEmployees.forEach(System.out::println);
System.out.println("\nEmployees earning more than 60,000:");
highEarners.forEach(System.out::println);
System.out.println("\nEmployees grouped by department:");
employeesByDept.forEach((dept, empList) -> System.out.println(dept + ": " + empList));
System.out.println("\nAverage salary by department:");
averageSalaryByDept.forEach((dept, avgSalary) -> System.out.println(dept + ": " + avgSalary));
System.out.println("\nEmployees sorted by name:");
sortedByName.forEach(System.out::println);
System.out.println("\nEmployee with the highest salary:");
highestEarner.ifPresent(System.out::println);
System.out.println("\nDistinct departments:");
distinctDepartments.forEach(System.out::println);
System.out.println("\nList of employee names:");
System.out.println(employeeNames);
System.out.println("\nConcatenated employee names:");
System.out.println(concatenatedNames);
System.out.println("\nAny employee earning more than 75,000?");
System.out.println(anyHighEarner);
System.out.println("\nTotal salary of all employees:");
System.out.println(totalSalary);
}
}
Output:
Employees in Engineering:
Employee{id=2, name='Bob', department='Engineering', salary=70000.0}
Employee{id=3, name='Charlie', department='Engineering', salary=80000.0}
Employees earning more than 60,000:
Employee{id=3, name='Charlie', department='Engineering', salary=80000.0}
Employee{id=2, name='Bob', department='Engineering', salary=70000.0}
Employees grouped by department:
HR: [Employee{id=1, name='Alice', department='HR', salary=45000.0}, Employee{id=4, name='David', department='HR', salary=50000.0}]
Engineering: [Employee{id=2, name='Bob', department='Engineering', salary=70000.0}, Employee{id=3, name='Charlie', department='Engineering', salary=80000.0}]
Finance: [Employee{id=5, name='Eve', department='Finance', salary=60000.0}, Employee{id=6, name='Frank', department='Finance', salary=55000.0}]
Average salary by department:
HR: 47500.0
Engineering: 75000.0
Finance: 57500.0
Employees sorted by name:
Employee{id=1, name='Alice', department='HR', salary=45000.0}
Employee{id=2, name='Bob', department='Engineering', salary=70000.0}
Employee{id=3, name='Charlie', department='Engineering', salary=80000.0}
Employee{id=4, name='David', department='HR', salary=50000.0}
Employee{id=5, name='Eve', department='Finance', salary=60000.0}
Employee{id=6, name='Frank', department='Finance', salary=55000.0}
Employee with the highest salary:
Employee{id=3, name='Charlie', department='Engineering', salary=80000.0}
Distinct departments:
HR
Engineering
Finance
List of employee names:
[Alice, Bob, Charlie, David, Eve, Frank]
Concatenated employee names:
Alice, Bob, Charlie, David, Eve, Frank
Any employee earning more than 75,000?
true
Total salary of all employees:
350000.0
Explanation of Key Methods
Filter:
- Filters elements based on a predicate.
stream.filter(e -> e.getDepartment().equals("Engineering"))
Map:
- Transforms elements in the stream.
stream.map(Employee::getName)
Collect:
- Gathers elements into a collection, map, or concatenated string.
stream.collect(Collectors.toList());
Grouping By:
- Groups elements by a classifier function.
stream.collect(Collectors.groupingBy(Employee::getDepartment));
Sorting:
- Sorts elements based on a comparator.
stream.sorted(Comparator.comparing(Employee::getName))
Reducing:
- Performs a reduction operation like finding the max or sum.
stream.max(Comparator.comparingDouble(Employee::getSalary))
Terminal Operations:
- Operations like
forEach,collect,findFirst, andreduceend the stream pipeline.
- Operations like