Collector.filtering và Collectors.flatMapping Trong Java 9

1. Overview

Stream.collect() là một trong các phương thức đầu cuối (terminal operation) của Stream API trong Java 8. Nó cho phép thực hiện các thao tác có thể thay đổi trên các phần tử được lưu giữ trong Stream. Chẳng hạn như: chuyển các phần tử sang một số cấu trúc dữ liệu khác, áp dụng một số logic bổ sung, tính toán, …

Lớp Java Collectors cung cấp nhiều phương thức khác nhau để xử lý các phần tử của Stream API. Chẳng hạn như:

  • Collectors.toList()
  • Collectos.toSet()
  • Collectors.toMap()
  • ...

Trong bài viết này, chúng ta sẽ đi khám phá 2 collectors đã được add vào trong java 9: Collectors.filteringCollectors.flatMapping được dùng với Collectors.groupingBy.

2. Filtering Collector

Collectors.filtering tương tự như Stream.filter(), nó được dùng để filter elements. Nếu Stream’s filter được dùng trong stream chain thì Collectors.filtering được thiết kế để sử dụng với groupingBy.

Với Stream’s filter, các values sẽ được filter trước rồi mới được group lại. Đối với cách này thì các values không thỏa điều kiện filter sẽ bị loại bỏ. Nếu chúng ta cần những giá trị bị loại bỏ đó thì phải thực hiện group trước khi filter, đó là cách mà Collectors.filtering thực hiện

List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 5);
Map<Integer, Long> resultUsingStreamFilter  = numbers
        .stream()
        .filter(item -> item > 3)
        .collect(Collectors.groupingBy(i -> i, Collectors.counting()));
System.out.println(resultUsingStreamFilter);

Sau khi chạy đoạn code trên, ta sẽ có kết quả:

{5=2}

Và khi sử dụng Collectors.filtering:

List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 5);
Map<Integer, Long> resultUsingCollector  = numbers
                .stream()
                .collect(Collectors.groupingBy(i -> i,
                        Collectors.filtering(item-> item > 3, Collectors.counting())));
System.out.println(resultUsingCollector);

Result:

{1=0, 2=0, 3=0, 5=2}

Như vậy, các giá trị 1, 2, 3 ở list numbers sẽ không bị loại bỏ đi.

3. FlatMapping Collector

Collectors.flatMapping tương tự như Collectors.mapping. Cả 2 sử dụng function và collector để collect các element, nhưng function flatMapping chấp nhận 1 stream của các element.

Giả sử ta có 1 class Blog

@Getter
@AllArgsConstructor
class Blog {
    private String authorName;
    private List<String> comments;
}

Blog blog1 = new Blog("1", Arrays.asList("Nice", "Very Nice"));
Blog blog2 = new Blog("2", Arrays.asList("Disappointing", "Ok", "Could be better"));
List<Blog> blogs = List.of(blog1, blog2);

Map<String,  List<List<String>>> authorComments1 = blogs
        .stream()
        .collect(
                Collectors.groupingBy(
                        Blog::getAuthorName,
                        Collectors.mapping(Blog::getComments, Collectors.toList()))
        );

System.out.println(authorComments1);

Result:

{1=[[Nice, Very Nice]], 2=[[Disappointing, Ok, Could be better]]}

Collectors.mapping sẽ thực hiện map toàn bộ comment của author thành 1 list của list. Nếu sử dụng flatMapping thì có thể loại bỏ được list này bằng cách sử dụng stream trực tiếp trên list comment

Blog blog1 = new Blog("1", Arrays.asList("Nice", "Very Nice"));
Blog blog2 = new Blog("2", Arrays.asList("Disappointing", "Ok", "Could be better"));
List<Blog> blogs = List.of(blog1, blog2);

Map<String, List<String>> authorComments2 = blogs
        .stream()
        .collect(
                Collectors.groupingBy(
                        Blog::getAuthorName,
                        Collectors.flatMapping(
                            blog -> blog.getComments().stream(), Collectors.toList())));

System.out.println(authorComments2);

Result

{1=[Nice, Very Nice], 2=[Disappointing, Ok, Could be better]}

Ngoài ra, collector này còn có thể sử dụng với Collectors.partitioningBy() để thực hiện chia đôi list input

Blog blog1 = new Blog("1", Arrays.asList("Nice", "Very Nice"));
Blog blog2 = new Blog("2", Arrays.asList("Disappointing", "Ok", "Could be better"));
List<Blog> blogs = List.of(blog1, blog2);

Map<Boolean, List<String>>  partition = blogs.stream()
        .collect(
                Collectors.partitioningBy(
                        item -> item.getAuthorName().equals("1"),
                        Collectors.flatMapping(
                                item -> item.getComments().stream(), Collectors.toList())));
System.out.println(partition);

Result:

{false=[Disappointing, Ok, Could be better], true=[Nice, Very Nice]}

4. Conclusion

Như vậy, qua bài viết trên chúng ta có thể thấy được cách sử dụng Collectors.filtering()Collectors.flatMapping() được sử dụng cùng với Collectors.groupingBy()Collectors.partitioningBy() từ java 9 trở lên.

https://www.baeldung.com/java9-stream-collectors


All Rights Reserved