Java8stream中利⽤groupingBy进⾏多字段分组求和案例
Java8的groupingBy实现集合的分组,类似Mysql的group by分组功能,注意得到的是⼀个map对集合按照单个属性分组、分组计数、排序
List items =Arrays.asList(\"apple\ \"apple\
// 分组
Map> result1 = items.stream().collect( Collectors.groupingBy( Function.identity() ));//{papaya=[papaya], orange=[orange], banana=[banana, banana], apple=[apple, apple, apple]}System.out.println(result1);
// 分组计数
Map result2 = items.stream().collect( Collectors.groupingBy(Function.identity(), Collectors.counting() ));
// {papaya=1, orange=1, banana=2, apple=3}System.out.println(result2);
Map finalMap = new LinkedHashMap<>();//分组, 计数和排序
result2.entrySet().stream()
.sorted(Map.Entry.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\ User user2 = new User(\"zhangsan\ User user3 = new User(\"lisi\ List list = new ArrayList(); list.add(user1); list.add(user2); list.add(user3);Map> 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\User user2 = new User(\"zhangsan\User user3 = new User(\"lisi\List list = new ArrayList();list.add(user1);list.add(user2);list.add(user3);Map>> 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中,我当前的第⼀次尝试是这样的,我知道这个解决⽅案类似于
Map>>>>>> 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> keyExtractor = wr ->Arrays.asList(wr.getFiveMinuteWindow(), wr.getCdn(), wr.getIsp(), wr.getResultCode(), wr.getTxnTime());Map, Integer> aggregatedData = webRecords.stream().collect(Collectors.groupingBy(keyExtractor, Collectors.summingInt(WebRecord::getReqBytes)));
在这种情况下,键是按固定顺序列出的5个元素。不是很⾯向对象,但很简单。或者,您可以定义⾃⼰的表⽰⾃定义键的类型,并创建适当的hashCode/equals实现。
补充知识:java8 新特性 Stream流 分组 排序 过滤 多条件去重 (最⼩、最⼤、平均、求和)什么是 Stream?
Stream 是⽤函数式编程⽅式在集合类上进⾏复杂操作的⼯具,其集成了Java 8中的众多新特性之⼀的聚合操作,开发者可以更容易地使⽤Lambda表达式,并且更⽅便地实现对集合的查找、遍历、过滤以及常见计算等。话不多说,直接上代码。
List list = new ArrayList();list = Arrays.asList(new User(\"⼩强\男\"), new User(\"⼩玲\⼥\"), new User(\"⼩虎\男\"), new User(\"⼩⾬\⼥\"), new User(\"⼩飞\男\"), new User(\"⼩玲\⼥\"));
//分组
Map> 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 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 userMap = list.stream().collect(Collectors.toMap(User::getName, User::getSex));System.out.println(JsonUtil.toJson(userMap))//取出所有名字List names = list.stream().map(User::getName).collect(Collectors.toList());System.out.println(JsonUtil.toJson(names))以上这篇Java8 stream 中利⽤ groupingBy 进⾏多字段分组求和案例就是⼩编分享给⼤家的全部内容了,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。