Stream API
1. Stream là gì?
Phần tiêu đề “1. Stream là gì?”Stream (Java 8+) cho phép xử lý một chuỗi phần tử (từ List, mảng,…) theo phong cách khai báo (declarative) - mô tả “làm gì” thay vì viết vòng lặp mô tả từng bước “làm như thế nào”:
import java.util.List;import java.util.stream.Collectors;
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenSquares = numbers.stream() .filter(n -> n % 2 == 0) // giữ lại số chẵn .map(n -> n * n) // bình phương từng số .collect(Collectors.toList()); // thu thập kết quả thành List
System.out.println(evenSquares); // [4, 16, 36, 64, 100]So với cách viết vòng lặp truyền thống làm cùng việc:
List<Integer> evenSquares2 = new ArrayList<>();for (int n : numbers) { if (n % 2 == 0) { evenSquares2.add(n * n); }}2. Các thao tác trung gian (intermediate) phổ biến
Phần tiêu đề “2. Các thao tác trung gian (intermediate) phổ biến”Thao tác trung gian trả về một Stream mới, có thể nối tiếp nhiều thao tác:
numbers.stream() .filter(n -> n > 5) // lọc theo điều kiện .map(n -> n * 2) // biến đổi từng phần tử .sorted() // sắp xếp .distinct() // loại trùng lặp .limit(3); // giới hạn số lượng phần tử3. Các thao tác kết thúc (terminal) phổ biến
Phần tiêu đề “3. Các thao tác kết thúc (terminal) phổ biến”Thao tác kết thúc “kích hoạt” toàn bộ pipeline và trả về kết quả cuối cùng (không phải Stream):
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
long count = numbers.stream().filter(n -> n > 2).count(); // đếm số lượngint sum = numbers.stream().mapToInt(Integer::intValue).sum(); // tổngboolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0); // có phần tử nào thỏa mãn?List<Integer> list = numbers.stream().collect(Collectors.toList()); // thu thập thành Listnumbers.stream().forEach(System.out::println); // duyệt và in4. ⚠️ Stream chỉ dùng được một lần
Phần tiêu đề “4. ⚠️ Stream chỉ dùng được một lần”Một Stream chỉ có thể tiêu thụ (consume) một lần - sau khi đã gọi một thao tác kết thúc, không thể tái sử dụng Stream đó nữa:
var stream = numbers.stream();stream.count(); // đã tiêu thụ streamstream.count(); // IllegalStateException: stream has already been operated upon or closedMuốn xử lý lại, phải tạo Stream mới từ numbers.stream().
5. Stream không sửa đổi nguồn dữ liệu gốc
Phần tiêu đề “5. Stream không sửa đổi nguồn dữ liệu gốc”Các thao tác trên Stream không thay đổi List/mảng gốc - luôn tạo ra kết quả mới:
List<Integer> original = List.of(1, 2, 3);List<Integer> doubled = original.stream().map(n -> n * 2).collect(Collectors.toList());
System.out.println(original); // [1, 2, 3] - KHÔNG đổiSystem.out.println(doubled); // [2, 4, 6]Tổng kết
Phần tiêu đề “Tổng kết”- Stream API xử lý dữ liệu theo phong cách khai báo:
filter,map,sorted,… rồi kết thúc bằngcollect/count/sum/… - Thao tác trung gian trả về Stream mới (có thể nối chuỗi); thao tác kết thúc trả về kết quả cuối
- Stream chỉ dùng được một lần - gọi thao tác kết thúc thứ hai sẽ ném lỗi
- Không sửa đổi dữ liệu nguồn - luôn tạo ra kết quả mới