Java8 stream 中利用 groupingBy 进行多字段分组求和案例

时间:2021-05-20

Java8的groupingBy实现集合的分组,类似Mysql的group by分组功能,注意得到的是一个map

对集合按照单个属性分组、分组计数、排序

List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya"); // 分组Map<String, List<String>> result1 = items.stream().collect( Collectors.groupingBy( Function.identity() ));//{papaya=[papaya], orange=[orange], banana=[banana, banana], apple=[apple, apple, apple]}System.out.println(result1); // 分组计数Map<String, Long> result2 = items.stream().collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ));// {papaya=1, orange=1, banana=2, apple=3}System.out.println(result2);Map<String, Long> finalMap = new LinkedHashMap<>(); //分组, 计数和排序result2.entrySet().stream() .sorted(Map.Entry.<String, Long>comparingByValue().reversed()) .forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue()));// {apple=3, banana=2, papaya=1, orange=1}System.out.println(finalMap);

集合按照多个属性分组

1.多个属性拼接出一个组合属性

public static void main(String[] args) { User user1 = new User("zhangsan", "beijing", 10); User user2 = new User("zhangsan", "beijing", 20); User user3 = new User("lisi", "shanghai", 30); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); Map<String, List<User>> collect = list.stream().collect(Collectors.groupingBy(e -> fetchGroupKey(e))); //{zhangsan#beijing=[User{age=10, name='zhangsan', address='beijing'}, User{age=20, name='zhangsan', address='beijing'}], // lisi#shanghai=[User{age=30, name='lisi', address='shanghai'}]} System.out.println(collect);} private static String fetchGroupKey(User user){ return user.getName() +"#"+ user.getAddress();}

2.嵌套调用groupBy

User user1 = new User("zhangsan", "beijing", 10);User user2 = new User("zhangsan", "beijing", 20);User user3 = new User("lisi", "shanghai", 30);List<User> list = new ArrayList<User>();list.add(user1);list.add(user2);list.add(user3);Map<String, Map<String, List<User>>> collect = list.stream().collect( Collectors.groupingBy( User::getAddress, Collectors.groupingBy(User::getName) ));System.out.println(collect);

3. 使用Arrays.asList

我有一个与Web访问记录相关的域对象列表。这些域对象可以扩展到数千个。

我没有资源或需求将它们以原始格式存储在数据库中,因此我希望预先计算聚合并将聚合的数据放在数据库中。

我需要聚合在5分钟窗口中传输的总字节数,如下面的sql查询

select round(request_timestamp, '5') as window, --round timestamp to the nearest 5 minute cdn, isp, http_result_code, transaction_time, sum(bytes_transferred)from web_recordsgroup by round(request_timestamp, '5'), cdn, isp, http_result_code, transaction_time

在java 8中,我当前的第一次尝试是这样的,我知道这个解决方案类似于Group by multiple field names in java 8

Map<Date, Map<String, Map<String, Map<String, Map<String, Integer>>>>>>> aggregatedData =webRecords .stream() .collect(Collectors.groupingBy(WebRecord::getFiveMinuteWindow, Collectors.groupingBy(WebRecord::getCdn, Collectors.groupingBy(WebRecord::getIsp, Collectors.groupingBy(WebRecord::getResultCode, Collectors.groupingBy(WebRecord::getTxnTime, Collectors.reducing(0, WebRecord::getReqBytes(), Integer::sum)))))));

这是可行的,但它是丑陋的,所有这些嵌套的地图是一个噩梦!要将地图“展平”或“展开”成行,我必须这样做

for (Date window : aggregatedData.keySet()) { for (String cdn : aggregatedData.get(window).keySet()) { for (String isp : aggregatedData.get(window).get(cdn).keySet()) { for (String resultCode : aggregatedData.get(window).get(cdn).get(isp).keySet()) { for (String txnTime : aggregatedData.get(window).get(cdn).get(isp).get(resultCode).keySet()) { Integer bytesTransferred = aggregatedData.get(window).get(cdn).get(distId).get(isp).get(resultCode).get(txnTime); AggregatedRow row = new AggregatedRow(window, cdn, distId...

如你所见,这是相当混乱和难以维持。

有谁知道更好的方法吗?任何帮助都将不胜感激。

我想知道是否有更好的方法来展开嵌套的映射,或者是否有一个库允许您对集合进行分组。

最佳答案

您应该为地图创建自定义密钥。最简单的方法是使用Arrays.asList:

Function<WebRecord, List<Object>> keyExtractor = wr -> Arrays.<Object>asList(wr.getFiveMinuteWindow(), wr.getCdn(), wr.getIsp(), wr.getResultCode(), wr.getTxnTime()); Map<List<Object>, Integer> aggregatedData = webRecords.stream().collect( Collectors.groupingBy(keyExtractor, Collectors.summingInt(WebRecord::getReqBytes)));

在这种情况下,键是按固定顺序列出的5个元素。不是很面向对象,但很简单。或者,您可以定义自己的表示自定义键的类型,并创建适当的hashCode/equals实现。

补充知识:java8 新特性 Stream流 分组 排序 过滤 多条件去重 (最小、最大、平均、求和)

什么是 Stream?

Stream 是用函数式编程方式在集合类上进行复杂操作的工具,其集成了Java 8中的众多新特性之一的聚合操作,开发者可以更容易地使用Lambda表达式,并且更方便地实现对集合的查找、遍历、过滤以及常见计算等。话不多说,直接上代码。

List<User> list = new ArrayList<User>();list = Arrays.asList( new User("小强", 11, "男"), new User("小玲", 15, "女"), new User("小虎", 23, "男"), new User("小雨", 26, "女"), new User("小飞", 19, "男"), new User("小玲", 15, "女"));//分组Map<String, List<User>> listMap = list.stream().collect(Collectors.groupingBy(User::getSex));for(String key:listMap.keySet()){ System.out.print(key+"组:"); listMap.get(key).forEach(user -> System.out.print(user.getName())); System.out.println();}//排序list.stream().sorted(Comparator.comparing(user-> user.getAge())) .forEach(user -> System.out.println(user.getName()));//过滤list.stream().filter(user -> user.getSex().equals("男")).collect(Collectors.toList()) .forEach(user -> System.out.println(user.getName()));//多条件去重list.stream().collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet<>( Comparator.comparing(user -> user.getAge() + ";" + user.getName()))), ArrayList::new)) .forEach(user -> System.out.println(user.getName()));//最小值Integer min = list.stream().mapToInt(User::getAge).min().getAsInt();//最大值Integer max = list.stream().mapToInt(User::getAge).max().getAsInt();//平均值Double average = list.stream().mapToInt(User::getAge).average().getAsDouble();//和Integer sum = list.stream().mapToInt(User::getAge).sum();System.out.println("最小值:"+min+", 最大值"+max+", 平均值:"+average+", 和:"+sum);//分组求和Map<String, IntSummaryStatistics> collect = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.summarizingInt(User::getAge)));IntSummaryStatistics statistics1 = collect.get("男");IntSummaryStatistics statistics2 = collect.get("女");System.out.println(statistics1.getSum());System.out.println(statistics1.getAverage());System.out.println(statistics1.getMax());System.out.println(statistics1.getMin());System.out.println(statistics1.getCount());System.out.println(statistics2.getSum());System.out.println(statistics2.getAverage());System.out.println(statistics2.getMax());System.out.println(statistics2.getMin());System.out.println(statistics2.getCount());//提取list中两个属性值,转为mapMap<String, String> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex));System.out.println(JsonUtil.toJson(userMap))//取出所有名字List<String> names = list.stream().map(User::getName).collect(Collectors.toList());System.out.println(JsonUtil.toJson(names))

以上这篇Java8 stream 中利用 groupingBy 进行多字段分组求和案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章