The first thing that comes to our mind when we talk about Java 8 is functional programming. But when we dive deeper inside Java8, we will unearth the concepts of processing Collections in an efficient and better way using Stream API.
This presentation explores the Stream APIs with various real time scenarios in the simplest way possible.
Please like if you find it helpful and leave a comment if you have any questions on Stream APIs or on the sections covered.
2. • Introduction
• Processing Collections - Java7 Vs Java8
• Introducing Java8 Stream APIs.
• Basic operations using Streams
• Advanced operations.
• Parallel stream.
• Read Stream of Lines from a File.
In This Tutorial ..
3. • Java Stream is an abstraction that lets you process data in a
declarative way.
• Perform SQL like operations on Collections.
• process really large collections efficiently By leverage Multi core
architectures.
What is Stream ?
4. Processing Collections - Java7 Vs Java8
• Find the list of employee ids sorted based on their Salary in a given
city.
List<Employee> employeeList =
StreamDemoHelper.getEmployeeList();
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
Each row in the above table represents Employee
Object.
5. List<Employee> employeeList = StreamDemoHelper.getEmployeeList(
List<Employee> melboureEmployees = new ArrayList<>();
for(Employee emp : employeeList){
if(emp.getCity().equals("MEL")){
melboureEmployees.add(emp);
}
}
Collections.sort(melboureEmployees, new Comparator<Employee>(){
@Override
public int compare(Employee o1, Employee o2) {
return o2.getSalary().compareTo(o1.getSalary());
}
});
List<Long> melEmpIdList = new ArrayList<>();
for(Employee emp : melboureEmployees){
melEmpIdList.add(emp.getEmployeeId());
}
Processing Collections - Java7
1. Iterate the list of Employees
and check each employees
city and add it to a separate
list.
2. Sort the list of Employees in
Melbourne based on salary.
3. Iterate Melbourne
employees and then add it
to separate list of ids.
Output: [1, 4, 3]
6. List<Long> empIds =
employeeList.stream()
.filter(emp -> emp.getCity().equals("MEL"))
.sorted(comparing(Employee::getSalary)
.reversed())
.map(emp -> emp.getEmployeeId())
.collect(toList());
Processing Collections – Java8
Only Declare what need to
be performed using Steam
APIs.
This code will produce the
same result as that of
previous code.
Don’t worry if you don’t
understand, we will see all the
Stream APIs in the next slides.
8. 1. Filter Employees based on city.
2. Total number of people whose salary greater than $230
3. Find List of Employee ids whose salary is less than $250 of a given
city.
4. Distinct Cities in which employees has salaries less than $250
5. Find total amount of money spent on Salaries Every Month.
6. Other operations..
Basic operations using Streams
9. Filter Employees based on city.
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
EMP ID City Salary
1 MEL 250
3 MEL 150
4 MEL 200
List<Employee> melEmpList = employeeList.stream()
.filter(emp -> emp.getCity().equals("MEL"))
.collect(Collectors.toList());
filter() method takes lamda expression of
Predicate type and filters the stream based on
the condition defined.
collect() method collects the filtered Steam objects
and converts it to List of Employee objects using
Collectors.toList() method defined inside it.
Collectors class has toSet(), toMap(), toConcurrentMap(), toCollection() methods to convert
the stream of objects to a particular type of collection.
10. Total number of people whose salary greater than $230
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
Long count = employeeList.stream()
.filter(emp -> emp.getSalary() > 230)
.count();
Filter employees whose salary is greater than
$250
count() will return the total of the filtered items.
count = 3
11. Find List of Employee ids whose salary is less than $250 of a
given city.
List<Long> melEmpIds = employeeList.stream()
.filter(emp -> emp.getCity().equals("MEL")
&& emp.getSalary() < 250)
.map(emp -> emp.getEmployeeId())
.collect(toList());
filter method actually return stream of employee
objects which is Stream<Employee>.
map() functions comes to our rescue to create a
stream of whatever we want based on the
employee object. map() function returns stream
of whatever that is returned in the lamda
expression that is passed to it. In our case map()
function returns Stream of employee ids.
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
EMP ID City Salary
1 MEL 250
3 MEL 150
4 MEL 200
[3, 4]
filter()
map()
12. some more examples of map() function…
List<Bonus> bonusList = employeeList.stream()
.map(emp -> new Bonus(emp.getEmployeeId(),
emp.getSalary() * 1.5))
.collect(toList());
The Lamda expression of map() function
returns Bonus object based on Employee
object and hence we get Stream of Bonus
objects which is Stream<Bonus>
whoAmI = employeeList.stream()
.map(emp -> {
Integer tax =calculateTaxes(emp);
//Date date = get payroll date.
return new Payroll(emp.getEmpId,
emp.geSalary,
date,
tax);
})
.collect(toList());
Guess what is the type of whoAmI ?
It is List<Payroll>
13. Find total amount of money spent on Salaries Every Month.
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
Long sumOfSalaries = employeeList.stream()
.mapToLong(emp -> emp.getSalary())
.sum();
sum() will return the total of all the salaries.
}Total = 1680
mapToLong() is similar to map() function except
map() returns Stream<Object> whereas
maptoLong returns LongStream object and helps
us perform sum() of all the Longs. Similarly, we have mapToInt() &
mapToDouble() to be used with
Integer and Double.
14. Distinct Cities in which employees has salaries less than $250
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
List<String> distinctCities =
employeeList.stream()
.filter(emp -> emp.getSalary() < 250)
.map(emp -> emp.getCity())
.distinct()
.collect(toList());
First filtering employees whose salary is less
than $300
distinct() method basically removes all the
duplicates.
MEL
MEL
SYD
SYD
filter()
[MEL,
SYD]
distinct()
15. Other Operations..
Check whether all the employees have salary
greater than $75. In this case it will return true.
Boolean allMatch = employeeList.stream()
.allMatch(emp -> emp.getSalary() > 75);
Double averageLong = employeeList.stream()
.collect(averagingLong(Employee::getSalary));
Highest & Lowest salaried employees.
Average salary in the company
Optional<Employee> minSalariedEmployee =
employeeList.stream()
.collect(minBy(comparing(Employee::getSalary)));
For maximum salaried employee use maxBy() function of Collectors class.
Please check Collectors method for other functions like reducing(), joining(), mapping() etc.
16. 1. Group employees based on whether salary greater than $250
2. Group employees based on City and order by Salary
3. Group employees based on city and then group each city by the
role of the employee and order by salary.
Advance operations using Streams
17. 1. Group employees based on whether salary greater than
$250
Map<Boolean , List<Employee> > partitionByMap =
employeeList.stream()
.collect(Collectors.partitioningBy(emp ->
emp.getSalary() > 250));
partitioningBy() method groups the collection
based on the codition passed to it. In this case
employees are grouped based on whether their
salary is greater than $250.
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
true [emp6, emp5]
false [emp1, emp3, emp2, emp7, emp4]
Map<Boolean, List<Employee>>
List<Employee>
18. Group employees based on City whose salary > 150 and order
by Salary
Map<String, List<Employee>> groupByCity =
employeeList.stream()
.filter(emp -> emp.getSalary() > 150)
.sorted(comparing(Employee::getSalary).reversed())
.collect(Collectors.groupingBy(emp -> emp.getCity()));
Collectors.groupingBy() is used to group the
collection based on the return type of the lamda
defined in it. In this case we are grouping based
on the City.
Just Note that in this case , grouping happens on
the filtered and sorted collection which we passed
to the collect method.
EMP ID City Salary
1 MEL 250
3 MEL 150
2 SYD 230
6 BRS 400
5 SYD 350
7 SYD 100
4 MEL 200
BRS [emp6]
MEL [emp1, emp4]
SYD [emp5, emp2]
Map<String, List<Employee>>
List<Employee>
20. Group employees based on city and then group each city by
the role of the employee and order by salary.
Map<String, Map<String, List<Employee>>>
groupByCityAndRole = employeeList.stream()
.sorted(comparing(Employee::getSalary).reversed())
.collect(groupingBy(emp -> emp.getCity(),
groupingBy(emp -> emp.getRole())));
Collectors.groupingBy() overloaded method
first takes initial criteria and second
parameter is Collectors object.
And hence we can use any method which
returns collectors object as second
parameter.. So, we passed another
groupingBy() method to further classify
employees based on the role in each city.
21. EMP ID City Salary Role
1 MEL 250 Developer
3 MEL 150 Developer
2 SYD 230 Developer
6 BRS 400 Director
5 SYD 350 Developer
7 SYD 100 Manager
4 MEL 200 Manager
List<Employee>
BRS MEL
DeveloperDirector Manager
[Emp6] [emp1, emp3] [Emp4]
SYD
Developer Manager
[emp5, emp2] [Emp7]
.. Continued
22. Parallel stream.
Note, that parallelism is not automatically faster than performing operations serially, although it can be if you have
enough data and processor cores.
List<Long> melEmpIds = employeeList.parallelStream()
.filter(emp -> emp.getCity().equals("MEL")
&& emp.getSalary() < 250)
.map(emp -> emp.getEmployeeId())
.collect(toList());
Aggregate operations and parallel streams enable
you to implement parallelism with non-thread-safe
collections provided that you do not modify the
collection while you are operating on it.
23. Read Stream of Lines from a File
Stream<String> streamOfLines = Files.lines(Paths.get(”/tmp/transaction.csv”));
The above code reads the file from path specified and creates stream of
Line. Each line is represented in the String format.