☰
Java 企业级工具类全家桶-48-分组聚合工具类
2026/10/9 7:56:37 网站建设 项目流程

📌拿来即用: Java 企业级工具类全家桶-48-分组聚合工具类

📋 目录

  • 一、工具类功能
  • 二、所需环境
  • 三、方法概览(按需调用)
  • 四、调用示例(直观体验)
  • 五、工具类源码(可直接复制)

一、工具类功能

给业务代码用的集合统计工具:一批明细拿回来就按门店、渠道、用户分组,顺手计数、求和、算均值、取每组最大最小、把重复键压成单值,还能按笔数出榜单,业务代码不用再手写 Stream 收集器和临时累加。

结果都是保持 key 首次出现顺序的可变 Map,null 键与 null 元素都能成组;数值按 double 累加,金额请改走 BigDecimal;榜单只按计数降序,不做分页与流式聚合。


二、所需环境

JDK 17或以上


三、方法概览(按需调用)

序号方法入参返回用途
1groupBy(collection, keyExtractor)待分组集合 + key提取函数Map<K, List>按 key 分组,值是该组全部元素
2countBy(collection, keyExtractor)待分组集合 + key提取函数Map<K, Long>分组计数,拿每个 key 的元素个数
3sumBy(collection, keyExtractor, mapper)待分组集合 + key提取函数 + 数值提取函数Map<K, Double>按 key 对指定数值字段求和
4averageBy(collection, keyExtractor, mapper)待分组集合 + key提取函数 + 数值提取函数Map<K, Double>按 key 对指定数值字段求均值
5maxBy(collection, keyExtractor, comparator)待分组集合 + key提取函数 + 元素比较器Map<K, T>取每个 key 组内的最大元素,并列时留先出现的
6minBy(collection, keyExtractor, comparator)待分组集合 + key提取函数 + 元素比较器Map<K, T>取每个 key 组内的最小元素,并列时留先出现的
7toMap(collection, keyExtractor)待转换集合 + key提取函数Map<K, T>每个 key 只留一个元素,重复键保留后出现的
8sortKeysByCountDesc(countMap)计数MapList按计数值降序出 key 榜单,计数相同的维持原顺序
9getGroup(groupedMap, key)分组Map + 要取的keyList安全取某一组的不可变视图,key 缺失给不可变空列表

四、调用示例(直观体验)

调用示例直接复制进 IDE 右键 Run 就能看到调用工具类各方法的输出结果。

21:00:31.351[main]INFO--「GroupAggregateUtils方法1groupBy(collection,keyExtractor)」返回分组Map->{望京店=[Order[shop=望京店,sku=A1,amount=38.0],Order[shop=望京店,sku=B7,amount=12.5],Order[shop=望京店,sku=A1,amount=7.5]],国贸店=[Order[shop=国贸店,sku=A2,amount=60.0],Order[shop=国贸店,sku=C3,amount=20.0]],西单店=[Order[shop=西单店,sku=A1,amount=99.0]]}21:00:31.379[main]INFO--「GroupAggregateUtils方法1groupBy(collection,keyExtractor)」null键与null元素都能成组->{null=[Order[shop=null,sku=X1,amount=5.0]],本条无门店=[null],望京店=[Order[shop=望京店,sku=X2,amount=8.0]]}21:00:31.380[main]INFO--「GroupAggregateUtils方法2countBy(collection,keyExtractor)」返回计数Map->{望京店=3,国贸店=2,西单店=1}21:00:31.383[main]INFO--「GroupAggregateUtils方法3sumBy(collection,keyExtractor,mapper)」返回求和Map->{望京店=58.0,国贸店=80.0,西单店=99.0}21:00:31.383[main]INFO--「GroupAggregateUtils方法4averageBy(collection,keyExtractor,mapper)」返回均值Map->{望京店=19.333333333333332,国贸店=40.0,西单店=99.0}21:00:31.384[main]INFO--「GroupAggregateUtils方法5maxBy(collection,keyExtractor,comparator)」返回每组最大一单->{望京店=Order[shop=望京店,sku=A1,amount=38.0],国贸店=Order[shop=国贸店,sku=A2,amount=60.0],西单店=Order[shop=西单店,sku=A1,amount=99.0]}21:00:31.384[main]INFO--「GroupAggregateUtils方法6minBy(collection,keyExtractor,comparator)」返回每组最小一单->{望京店=Order[shop=望京店,sku=A1,amount=7.5],国贸店=Order[shop=国贸店,sku=C3,amount=20.0],西单店=Order[shop=西单店,sku=A1,amount=99.0]}21:00:31.385[main]INFO--「GroupAggregateUtils方法7toMap(collection,keyExtractor)」返回key到单值Map(重复键留后出现的)->{望京店=Order[shop=望京店,sku=A1,amount=7.5],国贸店=Order[shop=国贸店,sku=C3,amount=20.0],西单店=Order[shop=西单店,sku=A1,amount=99.0]}21:00:31.385[main]INFO--「GroupAggregateUtils方法8sortKeysByCountDesc(countMap)」返回按笔数降序的门店榜->[望京店,国贸店,西单店]21:00:31.385[main]INFO--「GroupAggregateUtils方法9getGroup(groupedMap,key)」返回该组不可变视图->[Order[shop=望京店,sku=A1,amount=38.0],Order[shop=望京店,sku=B7,amount=12.5],Order[shop=望京店,sku=A1,amount=7.5]]21:00:31.386[main]INFO--「GroupAggregateUtils方法9getGroup(groupedMap,key)」key 不存在时->[]21:00:31.386[main]ERROR--「异常信息获取演示」集合入参为null->待分组集合不能为null21:00:31.386[main]ERROR--「异常信息获取演示」数值提取函数为null->数值提取函数不能为null21:00:31.386[main]ERROR--「异常信息获取演示」计数值含null->异常类型=NullPointerException21:00:31.387[main]ERROR--「异常信息获取演示」null元素撞上不兜底的提取器->异常类型=NullPointerException进程已结束,退出代码为0
packagecom.example.order.api;importcom.example.common.utils.GroupAggregateUtils;importlombok.extern.slf4j.Slf4j;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.Comparator;importjava.util.LinkedHashMap;importjava.util.List;importjava.util.Map;/** * 调用示例:整个文件复制进 IDE,右键 Run 就能看效果。 * * <p>分组聚合不依赖外部服务,示例用内存里的订单列表跑通全部入口; * 日志用 Lombok 的 {@code @Slf4j} + SLF4J 占位符,没装 Lombok 就把 {@code log} 换成项目自己的 Logger。 */@Slf4jpublicclassGroupAggregateUtilsDemo{/** 示例用的订单,实际换成你自己的实体类 */publicrecordOrder(Stringshop,Stringsku,doubleamount){}/** 六个订单:望京店三笔、国贸店两笔、西单店一笔,末笔与首笔同店同 SKU */privatestaticfinalList<Order>ORDERS=List.of(newOrder("望京店","A1",38.0),newOrder("国贸店","A2",60.0),newOrder("望京店","B7",12.5),newOrder("西单店","A1",99.0),newOrder("国贸店","C3",20.0),newOrder("望京店","A1",7.5));publicstaticvoidmain(String[]args){// ========== 一、分组与计数 ==========Map<String,List<Order>>grouped=GroupAggregateUtils.groupBy(ORDERS,Order::shop);log.info("「GroupAggregateUtils 方法1 groupBy(collection, keyExtractor)」返回分组Map -> {}",grouped);Map<String,List<Order>>nullFriendly=GroupAggregateUtils.groupBy(Arrays.asList(newOrder(null,"X1",5.0),null,newOrder("望京店","X2",8.0)),order->order==null?"本条无门店":order.shop());log.info("「GroupAggregateUtils 方法1 groupBy(collection, keyExtractor)」null 键与 null 元素都能成组 -> {}",nullFriendly);Map<String,Long>counts=GroupAggregateUtils.countBy(ORDERS,Order::shop);log.info("「GroupAggregateUtils 方法2 countBy(collection, keyExtractor)」返回计数Map -> {}",counts);// ========== 二、数值聚合 ==========Map<String,Double>sums=GroupAggregateUtils.sumBy(ORDERS,Order::shop,Order::amount);log.info("「GroupAggregateUtils 方法3 sumBy(collection, keyExtractor, mapper)」返回求和Map -> {}",sums);Map<String,Double>averages=GroupAggregateUtils.averageBy(ORDERS,Order::shop,Order::amount);log.info("「GroupAggregateUtils 方法4 averageBy(collection, keyExtractor, mapper)」返回均值Map -> {}",averages);// ========== 三、分组最值与取单值 ==========Comparator<Order>byAmount=Comparator.comparingDouble(Order::amount);Map<String,Order>highest=GroupAggregateUtils.maxBy(ORDERS,Order::shop,byAmount);log.info("「GroupAggregateUtils 方法5 maxBy(collection, keyExtractor, comparator)」返回每组最大一单 -> {}",highest);Map<String,Order>lowest=GroupAggregateUtils.minBy(ORDERS,Order::shop,byAmount);log.info("「GroupAggregateUtils 方法6 minBy(collection, keyExtractor, comparator)」返回每组最小一单 -> {}",lowest);Map<String,Order>lastOrderOfShop=GroupAggregateUtils.toMap(ORDERS,Order::shop);log.info("「GroupAggregateUtils 方法7 toMap(collection, keyExtractor)」返回key到单值Map(重复键留后出现的)-> {}",lastOrderOfShop);// ========== 四、榜单与安全取组 ==========List<String>ranked=GroupAggregateUtils.sortKeysByCountDesc(counts);log.info("「GroupAggregateUtils 方法8 sortKeysByCountDesc(countMap)」返回按笔数降序的门店榜 -> {}",ranked);List<Order>wangjing=GroupAggregateUtils.getGroup(grouped,"望京店");log.info("「GroupAggregateUtils 方法9 getGroup(groupedMap, key)」返回该组不可变视图 -> {}",wangjing);List<Order>notOpened=GroupAggregateUtils.getGroup(grouped,"未开业店");log.info("「GroupAggregateUtils 方法9 getGroup(groupedMap, key)」key 不存在时 -> {}",notOpened);// ========== 五、catch异常信息 ==========// 1. 集合入参为 nulltry{GroupAggregateUtils.groupBy(null,Order::shop);}catch(IllegalArgumentExceptione){log.error("「异常信息获取演示」集合入参为null -> {}",e.getMessage());}// 2. 数值提取函数为 null,消息直接点名是哪个入参try{GroupAggregateUtils.sumBy(ORDERS,Order::shop,null);}catch(IllegalArgumentExceptione){log.error("「异常信息获取演示」数值提取函数为null -> {}",e.getMessage());}// 3. 计数 Map 里混入 null 计数值,按契约抛 NullPointerExceptiontry{Map<String,Long>brokenCounts=newLinkedHashMap<>();brokenCounts.put("望京店",3L);brokenCounts.put("国贸店",null);GroupAggregateUtils.sortKeysByCountDesc(brokenCounts);}catch(NullPointerExceptione){log.error("「异常信息获取演示」计数值含null -> 异常类型={}",e.getClass().getSimpleName());}// 4. 集合里有 null 元素、key 提取器又没兜底时,提取器的异常不会被工具吞掉try{List<Order>dirty=newArrayList<>(ORDERS);dirty.add(null);GroupAggregateUtils.groupBy(dirty,Order::shop);}catch(NullPointerExceptione){log.error("「异常信息获取演示」null元素撞上不兜底的提取器 -> 异常类型={}",e.getClass().getSimpleName());}}}

五、工具类源码(可直接复制)

📦拿来即用: 48-分组聚合工具类(java企业级代码).zip

💡 工具类已调试通过,复制粘贴即可使用,省时省力

💡 可保存到自己网盘,永久有效


觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询