Sử dụng Stream peek() sao cho đúng cách

Java Stream API cung cấp rất nhiều method hỗ trợ các thao tác hằng ngày chúng ta phải đối mặt với các collection. Tuy nhiên trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu về peek() method thường hay bị nhầm lẫn bởi các lập trình viên mới làm quen với Stream API.

How peek work?

Giả sử có 1 class User:

public class User {
    private String name;
     // constructor, setter, getter
}

peek() method nhận 1 tham số là 1 implement của Consumer functional interface 

Stream<T> peek(Consumer<? super T> action);

Ví dụ xuất tên của tất cả các User trong List.

List<User> users = Arrays.asList(new User("Alice"), new User("Bob"), new User("Chuck"));
users.stream().peek(System.out::println);

Tuy nhiên, khi thực thi đoạn code trên, sẽ không có một dòng output nào xuất ra màn hình cho chúng ta. WHY?

Nhớ lại một chút về Stream API, gồm 3 phần: dữ liệu nguồn, intermediate operation, và cuối cùng là terminal operation.

Intermediate operation sẽ xử lý từng phần tử từ dữ liệu nguồn, tất cả các intermediate operation đều là lazy, và kết quả sẽ không có 1 intermediate operation nào thực thi cho đến khi Stream bắt đầu hoạt động. Để khởi động Stream hoạt động, chúng ta cần đến các terminal operation vì chúng là các hoạt động rút trích dữ liệu, các intermediate operation bắt buộc phải hoạt động để trả kết quả về cho chúng.

Sử dụng peek()

Thật ra peek() method thường được sử dụng với mục đích hỗ trợ debug, vì trong javadoc cũng có nói

peek()‘s Javadoc page says: “This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline“.

Quay trở lại với ví dụ ở trên của chúng ta, do không kết hợp với các terminal operation nào nên peek() method cũng không hoạt động. Vậy chúng ta chỉ cần thêm một vài terminal operation để có thể kiểm thử peek() method.

Ví dụ chuyển name của các User thành chữ cái thường và xuất ra màn hình.

public class test {
    public static void main(String[] agrs) {
        List<User> users = Arrays.asList(new User("Alice"), new User("Bob"), new User("Chuck"));

        List<String> names = users.stream()
                .filter(e -> e.getName().length() > 3)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(user -> user.getName().toLowerCase())
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(Collectors.toList());

        System.out.println(names);

    }
}

Hoặc chúng ta có thể sử dụng peek() method để cập nhập trực tiếp các giá trị của Object. Chúng ta có thể sử dụng map() để thay thế trong trường hợp này, tuỳ thuộc vào sở thích của các bạn.

List<User> users = Arrays.asList(new User("Alice"), new User("Bob"), new User("Chuck"));
        users.stream()
                .peek(u -> u.setName(u.getName().toLowerCase()))
                .forEach(user -> System.out.println(user.getName()));

Output:

alice
bob
chuck

Tóm lược

Một điểm cực kỳ quan trọng các bạn cần phải nhớ rằng các intermediate operation sẽ không thực hiện khi không được kết hợp với các terminal operation vì tụi nó sẽ nghỉ rằng “Tao tính toán xong cũng có ai dùng đâu” ở đây là terminal operation sẽ dùng các kết quả trả về tử intermediate operation.

Nguồn tham khảo

https://www.baeldung.com/java-streams-peek-api

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x