Giới thiệu về Java Stream API
1. Stream API là gì?
Một trong những tính năng chính của Java 8 là Stream - java.util.stream - một thư viện hỗ trợ trong việc xử lý một dãy các phần tử.
Stream còn được hiểu theo nghĩa là luồng, nhưng khác với nghĩa của luồng trong Thread. Stream là một luồng, một dãy, một dòng chảy các phần tử.
Stream được sử dụng để xử lý một tập hợp dữ liệu (array hoặc collection) mà không làm thay đổi dữ liệu gốc.
2. Stream và Collection
Stream và Collection thường đi chung với nhau, và cũng thường được so sánh với nhau. Stream khác Collection theo các cách sau đây:
No storage: Stream không phải là data-structure nên nó không lưu trữ bất kì phần tử nào. Nó chỉ truyền tải các phần tử từ một nguồn (source) thông qua một đường ống (pipeline) các hoạt động tính toán. Nguồn có thể là một collection, array, một phương thức generate ra dữ liệu hoặc một kênh I/O.
Functional in nature: Một tính toán trên Stream sẽ sinh ra một kết quả nhưng không làm thay đổi nguồn của nó. Ví dụ, việc filter một Stream sẽ sinh ra một Stream mới đã loại bỏ các phần tử cần lọc, hơn là việc xóa các phần tử cần lọc ra khỏi source.
Possibly unbounded: Trong khi Collection sẽ phải có kích thước hữu hạn thì Stream là vô hạn. Cơ bản là vì nó không chứa dữ liệu gì cả (no storage).
Consumable: Các phần tử trong Stream chỉ được duyệt qua duy nhất 1 lần trong suốt vòng đời của một Stream. Một Stream mới phải được tạo ra để duyệt lại các phần tử đó.
Nói gọn lại, Stream và Collection thường sẽ đi chung với nhau. Collection chứa data và có thể sẽ là nguồn phát data cho Stream. Stream nhận input, xử lý input bằng operation của Stream và return output đã qua xử lý.
3. Stream operations và pipelines
3.1. Stream operations
Stream operations được chia làm 2 loại: intermediate và terminal operations, và được kết hợp lại để tạo thành stream pipelines.
Intermediate operations trả về một stream mới. Mỗi operation sẽ có các tính toán khác nhau, nhưng đều nhận một input là stream và trả về một output cũng là stream. Output stream đó "chứa" các phần tử của source thỏa các điều kiện/tính toán của operation đó. Các intermediate operations thường được nối với nhau tạo thành pipeline, output stream của operation trước sẽ là input stream của operation sau. Một số intermediate operation như Stream.map, Stream.filter,...
Các intermediate operations này sẽ không thực thi bất kỳ gì mãi cho đến khi terminal operation khởi tạo. Đây được gọi là tính lazy của stream.
Terminal operations trả về một kết quả hoặc một side-effect :)) và chấm dứt luôn stream đó. Một khi một terminal operation được áp dụng lên stream, stream đó sẽ được xem là đã thực thi và không thể tái sử dụng lại. Một số terminal operation như Stream.sum (trả về kết quả), Stream.forEach (thực thi side-effect),...
3.2. Stream pipelines
Một stream pipeline bao gồm các thành phần chính:
Source: nguồn dữ liệu (array, collection,...)
Có thể có nhiều hoặc không có các bước trung gian - intermediate operations (Stream.filter, Stream.map,...)
Và một bước tổng hợp cuối cùng - terminal operation (Stream.forEach, Stream.reduce,...)