黑客日教程-Java8新功能:将数据集合分组,类似SQL的GROUPBY

黑客日教程-Java8新功能:将数据集合分组,类似SQL的GROUPBY插图1

1 介绍

本文将展示groupingBy收集器的多个示例。

阅读本文需要先准备Java Stream和Java收集器Collector的知识。

2 GroupingBy收集器

Java8的Stream API允许我们以声明的方式来处理数据集合。

静态工厂方法:Collectors.groupingBy(),以及Collectors.groupingByConcunrrent(),给我们提供了类似SQL语句中的”GROUP BY”的功能。这两个方法将数据按某些属性分组,并存储在Map中返回。

下面是几个重载的groupnigBy方法:

– 参数:分类函数

“`

static Collector>>

groupingBy(Function classifier)

“`

– 参数:分类函数,第二个收集器

“`

static Collector>

groupingBy(Function classifier,

Collector downstream)

“`

– 参数:分类函数,供应者方法(提供作为返回值的Map的实现),第二个收集器

“`

static > Collector

groupingBy(Function classifier,

Supplier mapFactory, Collector downstream)

“`

2.1 准备

先定义一个BlogPost类:

“`

class BlogPost {

String title;

String author;

BlogPostType type;

int likes;

}

“`

BlogPostType:

“`

enum BlogPostType {

NEWS,

REVIEW,

GUIDE

}

“`

BlogPost列表:

“`

List posts = Arrays.asList( … );

“`

2.2 根据单一字段分组

最简单的groupingBy方法,只有一个分类函数做参数。分类函数作用于strema里面的每个元素。分类函数处理后返回的每个元素作为返回Map的key。

根据博客文章类型来分组:

“`

Map> postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType));

“`

2.3 根据Map的key的类型分组

分类函数并没有限制返回字符串或标量值。返回map的key可以是任何对象。只要实现了其equals和hashcode方法。

下面示例根据type和author组合而成的Tuple实例来排序:

“`

Map> postsPerTypeAndAuthor = posts.stream()

.collect(groupingBy(post -> new Tuple(post.getType(), post.getAuthor())));

“`

2.4 修改返回Map的value的类型

groupingBy的第二个重载方法有一个额外的collector参数(downstream),此参数作用于第一个collector产生的结果。

如果只用一个分类函数做参数,那么默认会使用toList()这个collector来转换结果。

下面的代码显示地使用了toSet()这个collector传递给downstream这个参数,因此会得到一个博客文章的Set。

“`

Map> postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType, toSet()));

“`

2.5 根据多个字段分组

downstream参数的另外一个用处就是基于分组结果,做第二次分组。

下面代码,首先根据author分组,然后再根据type分组:

“`

Map> map = posts.stream()

.collect(groupingBy(BlogPost::getAuthor, groupingBy(BlogPost::getType)));

“`

2.6 得到分组结果的平均值

通过使用downstream,我们可以把集合函数应用到第一次分组的结果上。比如,获取到每种类型博客的被喜欢次数(likes)的平均值:

“`

Map averageLikesPerType = posts.stream()

.collect(groupingBy(BlogPost::getType, averagingInt(BlogPost::getLikes)));

“`

2.7 得到分组结果的总计

计算每种类型被喜欢次数的总数:

“`

Map likesPerType = posts.stream()

.collect(groupingBy(BlogPost::getType, summingInt(BlogPost::getLikes)));

“`

2.8 得到分组结果中的最大或最小值

我们还可以得到每种类型博客被喜欢次数最多的是多少:

“`

Map> maxLikesPerPostType = posts.stream()

.collect(groupingBy(BlogPost::getType,

maxBy(comparingInt(BlogPost::getLikes))));

“`

类似的,可以用minxBy得到每种类型博客中被喜欢次数最少的次数是多少。

注意:maxBy和minBy都考虑了当第一次分组得到的结果是空的场景,因此其返回结果(Map的value)是Optional

2.9 得到分组结果中某个属性的统计

Collectors API提供了一个统计collector,可以用来同时计算数量、总计、最小值、最大值、平均值等。

下面来统计一下不同类型博客的被喜欢(likes)这个属性:

“`

Map likeStatisticsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType,

summarizingInt(BlogPost::getLikes)));

“`

返回Map中的value,IntSummaryStatistics对象,包括了每个BlogPostType的文章次数、被喜欢总计、平均值、最大值、最小值。

2.10 把分组结果映射为另外的类型

更复杂的聚合操作可以通过应用一个映射downstream收集器到分类函数结果上来实现。

下面代码讲每类博客类型的标题连接起来了。

“`

Map postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType,

mapping(BlogPost::getTitle, joining(“, “, “Post titles: [“, “]”))));

“`

上面的代码,讲每个BlogPost实例映射为了其对应的标题,然后把博客标题的stream连接成了成了字符串,形如Post titles:[标题1,标题2,标题3]。

2.11 修改返回Map的类型

使用groupingBy的时候,如果我们要指定返回Map的具体类型,可以用第三个重载方法。通过传入一个Map供应者函数。

下面代码传入了一个EnumMap供应者函数,得到返回Map为EnumMap类型。

“`

EnumMap> postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType,

() -> new EnumMap<>(BlogPostType.class), toList()));

“`

3 并发的分组Collector

类似groupingBy,存在一个groupingByConcurrent收集器,可以利用到多核架构的能力。groupingByConcurrent也有3个重载的方法,与groupingBy类似。

但返回值必须是ConconcurrentHashMap或其子类。

要并发操作分组,那么stream也必须是并行的:

“`

ConcurrentMap> postsPerType = posts.parallelStream()

.collect(groupingByConcurrent(BlogPost::getType));

“`

注意:如果要提供一个Map供应者函数,必须保证函数返回的是ConconcurrentHashMap或其子类。

4 Java 9新功能

java9引入两个新的收集器可以在goupingBy中使用的:[更多详情]( baeldung.com/java9-stream-collectors)。

5 小结

本文讨论了Java 8 Collectors API中的groupingBy收集器的几个例子。

讨论了goupingBy如何对stream中的元素基于某个属性进行分组,以及如何返回结果。

示例代码见[github]( github.com搜索 eugenp)

下面的tutorials/tree/master/core-java-modules/core-java-8

黑客日教程-Java8新功能:将数据集合分组,类似SQL的GROUPBY插图3

> [编译]( baeldung.com啥玩儿意java-groupingby-collector )

举报/反馈

原创文章 黑客日教程-Java8新功能:将数据集合分组,类似SQL的GROUPBY,版权所有
如若转载,请注明出处:https://www.itxiaozhan.cn/20227540.html

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注