Tong Hoang Vu
Tong Hoang Vu's blog

Tong Hoang Vu's blog

Giới thiệu về Stream API

Giới thiệu về Stream API

Java Stream API

Tong Hoang Vu's photo
Tong Hoang Vu
·Sep 10, 2021·

5 min read

Subscribe to my newsletter and never miss my upcoming articles

Sau khi đã tìm hiểu qua 7 bài cơ bản trước đó, bây giờ chúng ta có thể bắt đầu học về Stream API. Bạn nào quên có thể coi lại nhé. Và hôm nay là bài đầu tiên, mình sẽ giới thiệu sơ lược các khái niệm chính trong Stream API (hơi nhiều lý thuyết tí).

1. Stream API là gì?

Đây là tính năng mới trên Java 8, cung cấp cho bạn một cách viết code mới theo hướng functional và declarative hơn. Stream API thường sử dụng khi xử lý các array hoặc collection, thay vì dùng vòng lặp for thì sử dụng Stream API sẽ hay hơn (dùng quen rồi thích lắm luôn ý 😍).

Trước hết, cần biết Stream là gì?

Stream có thể hiểu là một dãy liên tục các phần tử. Từ một stream có thể bị biến đổi qua nhiều bước và trở thành stream khác, hoặc được tổng hợp lại thành một output.

Để xử lý dữ liệu với stream, có 3 bước:

  • Lấy ra một stream từ một nguồn nào đó (array, collection,...)
  • Biến đổi stream qua nhiều bước trung gian (filter, map,...)
  • Tổng hợp stream thành output cần thiết

Cũng không nên so sánh giữa collection và stream. Giống như so sánh "táo và cam" vậy, không có nhiều ý nghĩa. Thực tế hai đứa này thường đi đôi với nhau, collection thì chứa dữ liệu còn stream thì xử lý dữ liệu đó, mục đích chúng khác nhau thì thôi không nên so sánh nhé.

Điểm quan trọng của stream là tính functional, nhận vào input và return output chứ không sửa đổi dữ liệu gốc.

2. Imperative và declarative

Để hiểu được lý do nên dùng Stream API, bạn cần biết được 2 programming paradism:

  • Imperative (dạng mệnh lệnh) là bạn phải viết hướng dẫn máy thực hiện từng bước cụ thể
  • Declarative (dạng khai báo) thì trừu tượng hơn, bạn chỉ cần miêu tả logic thực hiện (mong muốn đạt được gì qua các bước), mà không cần mô tả quá chi tiết từng bước

Ví dụ bài toán tính tổng các số bé hơn 10 một dãy số.

List<Integer> list = List.of(2, 3, 5, 7, 11, 13);

// Dạng mệnh lệnh, nêu các bước cụ thể
int sum1 = 0;
for (Integer num : list)
    if (num < 10)
        sum1 += num;

// Dạng khai báo, chỉ nêu ra mong muốn đạt được qua từng bước
int sum2 = list.stream()
    .filter(num -> num < 10) // Giữ lại các số mà nhỏ hơn 10
    .reduce(0, (sum, num) -> sum + num);
        // Ban đầu tổng sẽ là 0
        // Với mỗi số còn lại thì cộng dồn vào tổng

Đến đây các bạn có thể mường tượng được sự khác nhau giữa imperative và declarative paradism rồi. Với declarative chúng ta không cần quan tâm từng bước xử lý như thế nào, chỉ cần miêu tả được kết quả mong muốn qua từng bước. Nó giúp bạn focus hơn vào logic thực hiện, đỡ phải viết low level code.

Cũng không nên "thần thánh hóa" code declarative mà bỏ rơi imperative, mỗi loại sẽ có công dụng riêng. Trước đây code imperative cho cả việc "thực hiện lệnh" và "xử lý dữ liệu", nhưng từ khi có thêm declarative thì:

  • Code theo kiểu declarative để xử lý dữ liệu (sẽ hiệu quả hơn)
  • Code theo kiểu imperative khi thực hiện công việc nào đó khác (declarative không làm được)

3. Vài thứ linh tinh khác

3.1. Vì sao nên dùng Stream API?

Trước hết phải nói việc sử dụng stream có nghĩa là viết code theo dạng declarative. Ưu điểm thì như trên, xử lý dữ liệu hiệu quả, code ngắn và đơn giản. Ngoài ra, viết code Stream API cũng tận dụng được các ưu điểm của functional programming, code tốt hơn, tránh side effect, dễ dàng test.

Ngoài ra, stream API cũng dễ hơn cho Java để thực hiện tính toán song song (parallel). Bạn chỉ cần viết code với stream, rồi Java sẽ tự xử lý theo cách hợp lý. Còn nếu viết từng bước, theo kiểu imperative thì bạn phải tự viết xử lý parallel, Java không giúp được bạn trong trường hợp này.

3.2. Stream pipeline?

Cuối cùng, bạn nên nắm được về stream pipeline. Đơn giản nó chỉ là một đường ống thôi, input đưa vào, lọc qua nhiều bước và cho ra output. Pipeline sẽ gồm nhiều operation liên tục, stream đi vào từng operation và cho ra output cho tới hết.

Skärmavbild 2019-10-03 kl. 09.42.17.png

Có 2 loại operation:

  • Không có hoặc nhiều operation trung gian (intermediate): mỗi operation sẽ có chức năng nhất định, nhưng điểm chung là input vào stream và trả về stream đã biến đổi. Output stream của operation trước sẽ là input stream của operation tiếp theo.
  • Một terminal operation: tổng hợp lại thành kết quả cuối cùng và kết thúc. Kết quả tổng hợp có thể là 1 value duy nhất hoặc một array,...

Bài viết này đã bao quát gần như toàn bộ lý thuyết của Stream API. Đây là những phần khá quan trọng, hiểu được nó sẽ giúp bạn viết code stream tốt hơn. Vậy thôi, mình xin dừng bút ở đây và hi vọng mọi người tiếp tục ủng hộ mình nhé 😍

 
Share this