在本文中我们将使用通过多个例子来学习groupingBy
的使用。
2. GroupingBy
Java 8流式API可以让我们通过声明式的方式处理数据。
静态工厂方法Collectors.groupingBy()
与Collectors.groupingByConcurrent()
提供了类似于SQL语句中GROPU BY
所实现的功能。
上述方法用于根据对象的某个属性进行分组,并把结果存放在一个Map实例中。
groupingBy
函数具有以下这些重载定义:
- 使用一个分类函数作为参数
static <T,K> Collector<T,?,Map<K,List<T>>>
groupingBy(Function<? super T,? extends K> classifier)
- 使用一个分类函数与一个Collector作为参数
static <T,K,A,D> Collector<T,?,Map<K,D>>
groupingBy(Function<? super T,? extends K> classifier,
Collector<? super T,A,D> downstream)
- 使用一个分类函数、一个Supplier(用于提供用于存放结果的Map实现)以及一个Collector作为参数
static <T,K,D,A,M extends Map<K,D>> Collector<T,?,M>
groupingBy(Function<? super T,? extends K> classifier,
Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
2.1 示例代码准备
为了方便演示groupingBy()
的使用,让我们先定义一个BlogPost
类:
class BlogPost {
String title;
String author;
BlogPostType type;
int likes;
}
BlogPostType类:
enum BlogPostType {
NEWS,
REVIEW,
GUIDE
}
一个BlogPost列表:
List<BlogPost> posts = Arrays.asList( ... );
我们还要定义一个Tuple类,该类组合了type
与author
属性用于分类。
2.2 根据单个属性进行分组
让我们从最简单的groupingBy
方法开始,它只接受一个分类函数作为参数,这个分类函数将被应用于每一个元素上面,分类函数的返回值将被当做Map中的键。
Map<BlogPostType, List<BlogPost>> postsPerType = posts.stream()
.collect(groupingBy(BlogPost::getType));
2.3 根据对象进行分组
分类函数的返回值并没有被限定,你可以返回任何对象,只有我们确保我们正确实现了equals
与hashcode
方法。
同时根据type与author属性分组:
Map<Tuple, List<BlogPost>> postsPerTypeAndAuthor = posts.stream()
.collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor())));
PS:确保重写了Tuple类的equals
与hashcode
方法
2.4 修改返回的Map类型
第二个groupingBy的重载实现可以接受一个Collector作为参数。
PS:如2.2节所示,当我们只指定一个分类函数时,默认将使用toList()
作为Collector。
让我们使用toSet()
作为Collector来获得一个BlogPost对象的Set:
Map<BlogPostType, Set<BlogPost>> postsPerType = posts.stream()
.collect(groupingBy(BlogPost::getType, toSet()));
2.5 根据多个字段进行分组
groupingBy接受的Collector也可以是另外一个groupingBy
。
例如,先根据author分组,再根据type分组:
Map<String, Map<BlogPostType, List>> map = posts.stream()
.collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType)));
2.6 获取分组结果的平均值
我们可以把聚合函数作为参数传给groupingBy
的第二个参数。
例如,获取文章类型的点赞平均数量:
Map<BlogPostType, Double> averageLikesPerType = posts.stream()
.collect(groupingBy(BlogPost::getType, averagingInt(BlogPost::getLikes)));
2.7 获取分组结果的和
获取每种文章类型的点赞总数:
Map<BlogPostType, Integer> likesPerType = posts.stream()
.collect(groupingBy(BlogPost::getType, summingInt(BlogPost::getLikes)));
获取分组结果的最大值与最小值
获取每种文章类型下点赞数量最高的文章:
Map<BlogPostType, Optional<BlogPost>> maxLikesPerPostType = posts.stream()
.collect(groupingBy(BlogPost::getType,
maxBy(comparingInt(BlogPost::getLikes))));
PS:取最小值用minBy()
3. 总结
在本文中,我们已经通过几个例子学习了groupingBy
Collector的使用方法。