1. 概述

Java 8中引入了收集器(Collectors),它们帮助我们将输入元素积累到可变容器,如MapListSet中。

本文将探讨Java 9中新增的两个收集器:**Collectors.filteringCollectors.flatMapping**,它们与Collectors.groupingBy结合使用,提供智能元素集合功能。

2. filtering收集器

Collectors.filtering类似于Streamfilter()方法,用于过滤输入元素,但应用场景有所不同。Streamfilter通常用于流链中,而filtering作为收集器设计,常与groupingBy一起使用。

Streamfilter首先过滤值,然后进行分组。这样,被过滤掉的值将不再保留痕迹。如果需要保留过滤后的痕迹,应先分组再过滤,而这正是Collectors.filtering的作用。

Collectors.filtering接受一个过滤输入元素的函数和一个收集过滤元素的收集器:

@Test
public void givenList_whenSatifyPredicate_thenMapValueWithOccurences() {
    List<Integer> numbers = List.of(1, 2, 3, 5, 5);

    Map<Integer, Long> result = numbers.stream()
      .filter(val -> val > 3)
      .collect(Collectors.groupingBy(i -> i, Collectors.counting()));

    assertEquals(1, result.size());

    result = numbers.stream()
      .collect(Collectors.groupingBy(i -> i,
        Collectors.filtering(val -> val > 3, Collectors.counting())));

    assertEquals(4, result.size());
}

3. flatMapping收集器

Collectors.flatMapping类似于Collectors.mapping,但目标更精细。两者都接受一个函数和一个收集器,用于收集元素,但flatMapping的函数接收一个元素流,由收集器累积。

来看下面的模型类:

class Blog {
    private String authorName;
    private List<String> comments;
      
    // constructor and getters
}

Collectors.flatMapping允许我们跳过中间集合,直接将元素写入单个容器,这个容器是根据Collectors.groupingBy定义的分组。

@Test
public void givenListOfBlogs_whenAuthorName_thenMapAuthorWithComments() {
    Blog blog1 = new Blog("1", "Nice", "Very Nice");
    Blog blog2 = new Blog("2", "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())));
       
    assertEquals(2, authorComments1.size());
    assertEquals(2, authorComments1.get("1").get(0).size());
    assertEquals(3, authorComments1.get("2").get(0).size());

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

    assertEquals(2, authorComments2.size());
    assertEquals(2, authorComments2.get("1").size());
    assertEquals(3, authorComments2.get("2").size());
}

Collectors.mapping将所有作者的评论映射到收集器的容器(例如List),而flatMapping则移除了这个中间集合,因为它直接提供了评论列表流,以便映射到收集器的容器。

4. 总结

本篇文章展示了Java 9中新增的收集器Collectors.filteringCollectors.flatMapping如何与Collectors.groupingBy()一起使用。

这些收集器也可以与Collectors.partitioningBy()配合,但仅创建基于条件的两个分区,没有充分利用收集器的全部功能,因此本文未涉及这部分内容。

本教程中的代码片段完整源代码可在GitHub上获取。