Skip to content

Hướng dẫn Stream API

Khái quát

Stream là một abtract layer cho phép bạn xử lý một dòng dữ liệu dựa trên các thao tác đã định nghĩa trước. Bạn có thể tạo Stream từ các nguồn dữ liệu như CollectionsArrays hoặc I/O resources. Mặc định các lớp kế thừa của Collection đều có hàm .stream():

Collection<String> collection = Arrays.asList("hello", "loda", "kaka");
Stream<String> streamOfCollection = collection.stream(); // Tạo ra một stream từ collection
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); // tạo ra 1 luồng
Stream<String> parallelStream = list.parallelStream(); // luồng dữ liệu song song (xử lý trên nhiều thread cùng lúc)

Cách sử dụng

Chức năng của Stream là cực kì đa dạng giúp bạn thao tác dữ liệu dễ dàng hơn.

forEach(): Duyệt qua toàn bộ dữ liệu của bạn

list.stream().forEach(s -> System.out.println(s));

map(): Tạo ra các giá trị mới từ dữ liệu hiện có

Arrays.asList(3, 5, 7)
    .stream() // tạo ra Stream từ List<Integer>
    .map(i -> "loda-"+i) // biến đổi từng phần tử thành String
    .map(String::toUpperCase) // biến đổi từng phần tử thành Upper case
    .forEach(System.out::println); // in ra xem thử

filter() gíup chúng ta thao tác với những dữ liệu mong muốn.

Arrays.asList(2, 3, 5, 7)
    .stream()
    .filter(i -> i % 2 != 0) //từ đây trở đi, chúng ta chỉ muốn làm việc với số lẻ
    .map(i -> "loda-" + i)
    .map(String::toUpperCase)
    .forEach(System.out::println);

limit(): Giới hạn số lượng dữ liệu cần xử lý

IntStream.range(1, 1000).boxed() // Tạo ra Stream có dữ liệu từ 1->999
            .filter(i -> i % 2 != 0)
            .map(i -> "loda-" + i)
            .map(String::toUpperCase)
            .limit(10) // Chúng ta giới hạn lấy 10 cái rồi in ra
            .forEach(System.out::println);

sorted(): sắp xếp Stream. Bạn có thể tự định nghĩa cách sort bằng cách thêm Comparator vào

sorted((o1, o2) -> o1.compareTo(o2))
List<String> result = IntStream.range(1, 1000).boxed()
                                .filter(i -> i % 2 != 0)
                                .map(i -> "loda-" + i)
                                .map(String::toUpperCase)
                                .limit(10)
                                .sorted(Comparator.naturalOrder()) // một cách khác để sort
                                .collect(Collectors.toList());

collect() giúp chúng ta lấy toàn bộ dữ liệu đã biến đổi trong Stream thành đối tượng mình mong muốn.

List<String> result = Stream.of("bạn", "hãy", "like", "Fanpage", "loda","dể","cập","nhật","nhiều","hơn")
                            .filter(s -> {
                                System.out.println("[filtering] " + s);
                                return s.length()>=4;
                            })
                            .map(s -> {
                                System.out.println("[mapping] " + s);
                                return s.toUpperCase();
                            })
                            .limit(3)
                            .collect(Collectors.toList());
System.out.println("----------------------");
System.out.println("Result:");
result.forEach(System.out::println);
[filtering] bạn // không thoả mãn
[filtering] hãy // tiếp tục tìm, cũng k thoả mãn
[filtering] like // thoả mãn
[mapping] like // mapping nó luôn
[filtering] Fanpage // lại quay lại filter tìm tiếp, thoả mãn
[mapping] Fanpage // mapping
[filtering] loda // thoả mãn
[mapping] loda // mapping
// Đủ 3 trường hợp thoả mãn, dừng.
----------------------
Result:
LIKE
FANPAGE
LODA

Bản chất của Stream

(Có ví dụ trong bài gốc nữa)

Stream là Lazy evaluation. Hiểu đơn giản là nó sẽ không xử lý dữ liệu trực tiếp qua từng bước, mà chờ bạn khai báo xong tất cả các thao tác operation như mapfilter,v.v.. cho tới khi gặp lệnh .collect() thì nó thực hiện toàn bộ trong một vòng lặp duy nhất.

Hàm .collect() và một số hàm như min()max()count() được gọi là terminal operation. Khi gọi những function có dạng terminal thì Stream mới chính thức hoạt động.

Một lưu ý khi sử dụng là Stream không được tái sử dụng. Vì Stream được tạo ra để xử lý dữ liệu chứ không phải để lưu trữ! Nên muốn sử dụng, mỗi lần bạn sẽ cần tạo ra 1 Stream mới.

Stream<String> stream =
  Stream.of("loda", ".", "me","like").filter(element -> element.contains("e"));
Optional<String> anyElement = stream.findAny(); //Lấy ra một phần tử bất kỳ trong Stream, nó sẽ trả ra Optional

// Thực hiện dòng lệnh tiếp theo sẽ bắn ra IllegalStateException
Optional<String> firstElement = stream.findFirst();