前言

本文将展示groupingBy收集器的多个示例,阅读本文需要先准备Java Stream和Java收集器Collector的知识。

一、GroupingBy收集器

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

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

作为collect方法的参数,Collector是一个接口,它是一个可变的汇聚操作,将输入元素累计到一个可变的结果容器中;它会在所有元素都处理完毕后,将累积的结果转换为一个最终的表示(这是一个可选操作);

Collectors本身提供了关于Collector的常见汇聚实现,Collectors的内部类CollectorImpl实现了Collector接口,Collectors本身实际上是一个工厂。

Collector 类方法:

public interface Collector {

Supplier supplier();

BiConsumer accumulator();

BinaryOperator combiner();

Function finisher();

Set characteristics();

}

Collector主要定义了容器的类型,添加元素的方法,容器合并的方法还有输出的结果。

supplier就是生成容器

accumulator是添加元素

combiner是合并容器

finisher是输出的结果

characteristics是定义容器的三个属性(三个枚举值),包括是否有明确的finisher,是否需要同步,是否有序。

CONCURRENT(集合的操作需要同步):表示中间结果只有一个,即使在并行流的情况下。所以只有在并行流且收集器不具备CONCURRENT特性时,combiner方法返回的lambda表达式才会执行(中间结果容器只有一个就无需合并)

UNORDER(集合是无序的):表示流中的元素无序。

IDENTITY_FINISH(不用finisher):表示中间结果容器类型与最终结果类型一致,此时finiser方法不会被调用

其中这里的泛型所表示的含义是:

T:表示流中每个元素的类型。

A:表示中间结果容器的类型。

R:表示最终返回的结果类型。

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

参数 :分类函数

static Collector>>

groupingBy(Function super T,? extends K> classifier)

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

static Collector>

groupingBy(Function super T,? extends K> classifier,

Collector super T,A,D> downstream)

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

static > Collector

groupingBy(Function super T,? extends K> classifier,

Supplier mapFactory, Collector super T,A,D> downstream)

二、使用示例

2.1 准备

先定义一个BlogPost类:

class BlogPost {

String title;

String author;

BlogPostType type;

int likes;

}

BlogPostType:

enum BlogPostType {

NEWS,

REVIEW,

GUIDE

}

public class Tuple {

String author;

BlogPostType type;

public String getAuthor() {

return author;

}

public void setAuthor(String author) {

this.author = author;

}

public BlogPostType getType() {

return type;

}

public void setType(BlogPostType type) {

this.type = type;

}

}

BlogPost列表:

List posts = Arrays.asList( ... );

2.2 根据单一字段分组

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

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

Map> postsPerType = posts.stream()

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

groupingBy(BlogPost::getType) 默认返回的是toList。

2.3 根据Map的key的类型分组

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

下面示例根据type和author组合而成的BlogPost实例来分组:

Map> postsPerTypeAndAuthor = posts.stream()

.collect(groupingBy(post -> new BlogPost()));

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 修改返回自定义类型

mapping函数:mapper:返回参数对象,downstream收集的集合值。

Collector mapping(Function super T, ? extends U> mapper,

Collector super U, A, R> downstream)

示例代码:

Map>> postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType, mapping(n->getBlogPost(n), toList())));

privatestatic Map getBlogPost(BlogPost blogPost) {

Map map = new HashMap<>();

map.put("title", blogPost.getTitle());

return map;

}

mapping() 收集器,自定义返回。

2.6 根据多个字段分组

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

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

Map> map = posts.stream()

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

2.7 得到分组结果的平均值

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

Map averageLikesPerType = posts.stream()

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

2.8 得到分组结果的总计

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

Map likesPerType = posts.stream()

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

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

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

Map> maxLikesPerPostType = posts.stream()

.collect(groupingBy(BlogPost::getType,

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

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

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

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

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

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

Map likeStatisticsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType,

summarizingInt(BlogPost::getLikes)));

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

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

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

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

Map postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType,

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

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

2.12 修改返回Map的类型

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

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

EnumMap> postsPerType = posts.stream()

.collect(groupingBy(BlogPost::getType,

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

2.13 collectingAndThen包裹一个收集器,对其结果应用转换函数

public static Collector collectingAndThen(Collector downstream,

Function finisher)

示例代码

//对分组进行转换,对分组内元素进行计算

Map> postsPerTypeList = posts.stream()

.collect(groupingBy(BlogPost::getType, collectingAndThen(toList(), m->{

Map map = new HashMap<>();

map.put("count", m.stream().count());

//对分组的list求和

map.put("money", m.stream().mapToDouble(BlogPost::getLikes).sum());

return map;

})));

3 并发的分组Collector

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

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

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

ConcurrentMap> postsPerType = posts.parallelStream()

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

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

总结

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

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

本文地址:https://blog.csdn.net/xp_lx1/article/details/109624697

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

java 多字段分组_java8 stream统计、汇总、多字段分组、多个列汇总统计相关推荐

  1. stream对多个字段分组_java8 stream 如何按多字段分组,并对一个字段求和

    第一次回答,希望能帮到你 User类: class User { String name; String phone; String address; Long scope; public User( ...

  2. c# list集合根据某个字段去重_java8 List 根据对象某个字段或多个字段去重、筛选、List转Map、排序、分组、统计计数等等...

    我们利用 java8 的新特性,可以方便简洁高效的处理一些集合的数据. 简单示例如下: 先定义一个订单对象(Order) public class Order { private Long id; p ...

  3. stream对多个字段分组_Java8 stream 中利用 groupingBy 进行多字段分组

    1.利用stream对数据进行分组并求和 public static void main(String[] args) { List items = Arrays.asList("apple ...

  4. Linux 文本 列数 统计

    Linux 文本 列数统计 方法一:(非等列数据统计) $cat 1.txt | awk '{print NF}' 444 方法二:(前提是等列数据表) $cat 1.txt | awk 'END{p ...

  5. stream 过滤俩个字段_Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合...

    点波关注不迷路,一键三连好运连连! 先贴上几个案例,水平高超的同学可以挑战一下: 从员工集合中筛选出salary大于8000的员工,并放置到新的集合里. 统计员工的最高薪资.平均薪资.薪资之和. 将员 ...

  6. java 根据条件从list中筛选出符合条件的集合_Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合

    点波关注不迷路,一键三连好运连连! 先贴上几个案例,水平高超的同学可以挑战一下: 从员工集合中筛选出salary大于8000的员工,并放置到新的集合里. 统计员工的最高薪资.平均薪资.薪资之和. 将员 ...

  7. stream 多个字段分组_Python Pandas对Excel数据的分组聚合和数据透视

    使用Excel进行商业数据分析的时候,最重要的就是两个手段就是vlookup函数和数据透视表.本章就讲解一下与数据透视功能相关的分组聚合和数据透视.其实分组聚合和数据透视两者基本是等价的,但由于使用的 ...

  8. java strem 分组并提取对象中的某个字段

    先上代码 import java.util.*; import java.util.stream.Collectors;/*** @author jnchen* @date 2021/3/10 11: ...

  9. 计算一班总分 使用的计算机公式是,班级总分统计excle!excle如图所示,怎样按照班级字段,将每班的数学语文英语分数分别求和汇总?...

    在excle中,如何计算一个班级总人数的80%人数(分数从高开始的前)的总分和平均分. 自动排啊 EXCLE中怎么把未打卡体温的学生名单按班级统计出来? 建议用代码来解决. excle怎么利用公式统计 ...

最新文章

  1. Mysql5.5配置主从复制
  2. RESTful Web 服务 - 方法
  3. android+apk反编译+Mac
  4. Linux内核设计与实现---进程调度
  5. 日志级别_SpringBoot实战(十三):Admin动态修改日志级别
  6. oracle如何设置权限,Oracle创建用户并设置权限
  7. 35+大龄程序员被清退?
  8. 卡盟销售官网源码php,卡盟整站程序源码 php版
  9. 如何用Scapy写一个端口扫描器?
  10. 差分码、相对码、绝对码、空号差分码、传号差分码
  11. ie tab chrome_将IE Tab集成添加到Google Chrome
  12. QtDBus快速入门
  13. html字体白色边框黑色效果,css完整总结:第二篇(尺寸,外补白,内补白,边框,背景,颜色,字体,文本,文本装饰)...
  14. 记一次HDD(机械硬盘)突然出故障,然后数据恢复以及更换HDD的过程
  15. gmail imap_阻止带有Gmail IMAP的Outlook在待办事项栏中显示重复的任务
  16. 云服务器可以通过远程打游戏吗,云主机能玩游戏吗_云主机安全防护措施
  17. 2021.10.9小米一面
  18. Hash 哈希 PTA 相关题目解析
  19. 阿里巴巴离职DBA_35岁总结的职业生涯
  20. bitset 用法整理

热门文章

  1. 基于JAVA+Servlet+JSP+MYSQL的实验室机房预约管理系统
  2. 基于JAVA+SpringMVC+Mybatis+MYSQL的宠物商城
  3. 2018-2019-2 网络对抗技术 20165328 Exp4 恶意代码分析
  4. Django学习之十: staticfile 静态文件
  5. Oracle中如何判断字符串是否全为数字
  6. 文件夹文件遍历并插入数据库的操作,IO Directory File的递归操作
  7. ArcSDE服务入门
  8. 计算机网络技术之网络系统设计与组建工程
  9. 初学者python笔记(类的内置属性)
  10. Navicate ---error 2003: can‘t connect to mysql server on ‘localhost‘(10061)“