Tìm hiểu cơ chế Lazy Evaluation của Stream trong Java 8
Bài viết được sự cho phép của tác giả Giang Phan
Trong bài viết “Giới thiệu về Stream API trong Java 8” , chúng ta đã tìm hiểu về các đặc điểm, các làm việc của Stream trong Java 8. Ở bài viết này, tôi muốn giải thích kỹ hơn về cơ chế Lazy Evaluation của Stream trong Java 8.
Xem thêm tuyển dụng Java lương cao trên TopDev
Như chúng ta đã biết, Stream có một đặc điểm rất quan trọng là cho phép tối ưu hóa hiệu xuất của chương trình thông qua cơ chế lazy evaluation, nghĩa là chúng không được thực hiện cho đến khi cần thiết. Các hoạt động tính toán trên source data chỉ được thực hiện khi một terminal operation được khởi tạo và các source element chỉ được sử dụng khi cần.
Hãy xem ví dụ sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; import java.util.stream.Stream; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor class Employee { String name; String department; int salary; } class EmployeeRepository { private static final Map<Integer, Employee> employees = new HashMap<>(); static { employees.put( 1 , new Employee( "gpcoder 1" , "A" , 50 )); employees.put( 2 , new Employee( "gpcoder 2" , "B" , 100 )); employees.put( 3 , new Employee( "gpcoder 3" , "A" , 150 )); employees.put( 4 , new Employee( "gpcoder 4" , "B" , 200 )); } public Employee findById(Integer id) { System.out.println( "findById: " + id); return employees.get(id); } } class EmployeeFilter { public static Predicate<Employee> filterDepartmentEqualsWith(String department) { return e -> { System.out.println( "filterDepartmentEqualsWith: " + e); return e.getDepartment().equals(department); }; } public static Predicate<Employee> filterSalaryGreaterThan( int salary) { return e -> { System.out.println( "filterSalaryGreaterThan: " + e); return e.getSalary() >= salary; }; } } public class Java8StreamDeeply { public static void main(String[] args) { EmployeeRepository employeeRepository = new EmployeeRepository(); Integer[] empIds = { 1 , 2 , 3 , 4 }; Stream.of(empIds) .map(employeeRepository::findById) .filter(EmployeeFilter.filterSalaryGreaterThan( 100 )) .filter(EmployeeFilter.filterDepartmentEqualsWith( "A" )) .findFirst(); } } |
Chạy chương trình trên, chúng ta có kết quả như sau:
1
2
3
4
5
6
7
8
|
findById: 1 filterSalaryGreaterThan: Employee(name=gpcoder 1 , department=A, salary= 50 ) findById: 2 filterSalaryGreaterThan: Employee(name=gpcoder 2 , department=B, salary= 100 ) filterDepartmentEqualsWith: Employee(name=gpcoder 2 , department=B, salary= 100 ) findById: 3 filterSalaryGreaterThan: Employee(name=gpcoder 3 , department=A, salary= 150 ) filterDepartmentEqualsWith: Employee(name=gpcoder 3 , department=A, salary= 150 ) |
Như bạn thấy, chương trình của chúng ta có 4 phần tử nhưng chỉ 3 phần tử được thực thi. Tại sao vậy?
Trong ví dụ trên, tôi sử dụng 3 Intermediate operations: 1 map() và 2 filter() operations. Chúng ta có 4 phần tử ở Stream source, mỗi phần tử có thể sẽ được thực thi 1 lần qua tất cả intermediate operation.
Đầu tiên, nó sẽ kiểm tra tất cả các operation trên phần tử có id là 1. Vì salary của nó không thõa filter salary, nên xử lý sẽ chuyển sang phần tử kế tiếp – id là 2. Không thực hiện trên filter deparment.
Tiếp theo, id 2 chỉ thõa mãn điều kiện salary, không thõa mãn điều kiện department, nên xử lý chuyển sang phần tử kế tiếp – id là 3.
Tại id 3, thõa mãn tất cả điều kiện ở trên và Stream evaluate yêu cầu Terminal Operations findFirst() và trả về kết quả.
Các operation trên các phần tử còn lại sẽ không được thực thi – id 4.
Có thể thấy rằng, Stream hoạt động tuần tự trên từng phần tử của source, duyệt qua tất cả các immediate operations, rồi đến terminal operation, cuối cùng mới chuyển sang phần tử kế tiếp (có thể không chuyển sang phần tử kế tiếp nếu đã thõa mãn mong muốn của terminal operation).
Tóm lại, quá trình xử lý các Stream một cách lười biếng (lazy) cho phép tránh việc kiểm tra tất cả dữ liệu khi không cần thiết. Cách làm này giúp cho Stream hoạt động rất hiệu quả khi có nhiều intermediate operation và nguồn dữ liệu là lớn.
Bài viết đến đây là hết, sau bài này hy vọng các bạn hiểu rõ hơn về Stream trong Java 8 và vận dụng nó thích hợp để đạt được hiệu quả tốt hơn.
Bài viết gốc được đăng tải tại gpcoder.com
Có thể bạn quan tâm:
- Cùng tìm hiểu về Stream trong Java 8
- Streaming Media với Nginx và nginx-rtmp module
- Java Stream – Collectors và Statistics
Xem thêm tuyển dụng các vị trí CNTT hấp dẫn trên TopDev
- T Từ khóa final trong Java
- R RxJava – Thời đại lập trình Reactive programming ứng dụng Android đã tới
- J Java roadmap cho newbie – Từ số 0 đến chuyên nghiệp
- T Top các Framework Java Backend phổ biến cho anh em Developer
- T Tìm hiểu về lớp HashMap trong Java: Các hoạt động và cách dùng
- L Lập trình Android bằng Java: Hướng dẫn toàn diện
- J Java Super là gì? Sự khác biệt giữa Java Super và Java thông thường
- K Khai báo phương thức overloading trong Java
- J Java Sleep: Kỹ thuật ngủ luồng trong lập trình Java
- S Sử dụng subString trong xử lý văn bản trong Java