JDK 8 list分组获取第一个元素

概述


JDK8 List分组一文中介绍了JDK 8如何对list进行分组,但是没有提到如何在分组后,获取每个分组的第一个元素。其实这个也很简单,代码如下:

package test;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ListGroupFindFirstTest3 {
    public static void main(String[] args) {
        List<Coupon> couponList = new ArrayList<>();
        Coupon coupon1 = new Coupon(1,100,"优惠券1");
        Coupon coupon2 = new Coupon(2,200,"优惠券2");
        Coupon coupon3 = new Coupon(3,300,"优惠券3");
        Coupon coupon4 = new Coupon(3,400,"优惠券4");
        couponList.add(coupon1);
        couponList.add(coupon2);
        couponList.add(coupon3);
        couponList.add(coupon4);

        Map<Integer, Coupon> resultList = couponList.stream().collect(Collectors.groupingBy(Coupon::getCouponId,Collectors.collectingAndThen(Collectors.toList(),value->value.get(0))));
        System.out.println(JSON.toJSONString(resultList, SerializerFeature.PrettyFormat));
    }
}

package test;

public class Coupon {
    private Integer couponId;
    private Integer price;
    private String name;

    public Coupon(Integer couponId, Integer price, String name) {
        this.couponId = couponId;
        this.price = price;
        this.name = name;
    }

    public Integer getCouponId() {
        return couponId;
    }

    public void setCouponId(Integer couponId) {
        this.couponId = couponId;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

需要借助Collectors.collectingAndThen方法,对组内的元素进行处理,这里是获取第一个元素。

代码输出结果如下:

{   1:{
		"couponId":1,
		"name":"优惠券1",
		"price":100
	},
	2:{
		"couponId":2,
		"name":"优惠券2",
		"price":200
	},
	3:{
		"couponId":3,
		"name":"优惠券3",
		"price":300
	}
}

<think>我们有一个需求:根据teamCode对列表进行分组统计,并找出分数最大的组。步骤:1.首先,我们需要一个包含teamCode和分数的对象列表。假设我们有一个类,比如叫TeamScore,包含两个属性:teamCode和score。2.使用StreamAPI,通过Collectors.groupingBy按teamCode分组。3.然后,我们需要对每个分组内的分数进行求和(或者可能是其他统计,如平均?但题目要求找出分数最大的组,这里我们理解为每个组的总分最大)。但是题目没有明确是求每个组的总分最大还是最高分?根据常见需求,我们通常求总分最大的组。4.分组后,我们使用Collectors.summingInt(或summingLong等,根据score的类型)来对每个组的分数求和。5.然后,我们遍历分组后的Map,找到值(即总分)最大的那个组。但是,也可以一步到位:在分组的同时进行求和,然后使用Collectors.collectingAndThen结合maxBy来直接找到最大值?但是注意,分组后是一个Map,我们需要遍历Map来找到最大值。另一种方法是:在分组求和后,将Map的EntrySet转为Stream,然后通过max比较器找到最大值的Entry。因此,我们可以分两步:第一步:分组并计算每个组的总分。第二步:从分组结果中找到总分最大的组。示例代码:假设数据对象为:classTeamScore{privateStringteamCode;privateintscore;//假设分数是整数//构造方法、getter和setter省略}步骤:1.创建List<TeamScore>。2.使用Collectors.groupingBy按teamCode分组,并使用Collectors.summingInt对每个组的分数求和。3.然后,我们得到一个Map<String,Integer>,键为teamCode,值为该组的总分。4.然后,我们遍历这个Map的entrySet,使用Stream的max方法,通过比较Map.Entry的值(即总分)来找到最大总分的组。但是,我们也可以使用Collectors.toMap然后直接收集为Map,再找最大值。或者使用Collectors.collectingAndThen在分组时直接返回最大值?但这样会丢失其他组的信息,所以通常我们分组后然后最大值。注意:有可能有多个组的总分相同且都是最大,那么我们可以选择第一个遇到的,或者都保留。这里题目要求找出分数最大的组,我们其中一个(如果有多个最大,其中一个即可)。示例代码:分组并求和:Map<String,Integer>teamTotalScores=teamScores.stream().collect(Collectors.groupingBy(TeamScore::getTeamCode,Collectors.summingInt(TeamScore::getScore)));然后,从teamTotalScores中找到总分最大的组:Optional<Map.Entry<String,Integer>>maxEntry=teamTotalScores.entrySet().stream().max(Map.Entry.comparingByValue());这样,maxEntry就包含了总分最大的组的teamCode和总分。但是,如果我们还需要这个组的所有原始数据(而不仅仅是总分),那么分组时就不要只求和,而是保留整个组的数据,然后计算每个组的总分,再比较。然而,题目要求是分组统计并找出分数最大的组。这里“统计”我们可以理解为求和,然后找出总分最大的组。所以,如果只需要teamCode和总分,那么上面的方法就可以了。如果还需要这个组的所有对象,那么我们可以这样:Map<String,List<TeamScore>>grouped=teamScores.stream().collect(Collectors.groupingBy(TeamScore::getTeamCode));//然后计算每个组的总分,并找到总分最大的组StringmaxTeamCode=null;intmaxTotal=Integer.MIN_VALUE;List<TeamScore>maxTeamList=null;for(Map.Entry<String,List<TeamScore>>entry:grouped.entrySet()){inttotal=entry.getValue().stream().mapToInt(TeamScore::getScore).sum();if(total>maxTotal){maxTotal=total;maxTeamCode=entry.getKey();maxTeamList=entry.getValue();}}但是,使用StreamAPI,我们可以:Optional<Map.Entry<String,List<TeamScore>>>maxEntry=grouped.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,e->e.getValue().stream().mapToInt(TeamScore::getScore).sum())).entrySet().stream().max(Map.Entry.comparingByValue());但是这样又回到了第一种方式,而且效率低(因为分组后我们又遍历每个组求和,然后又求最大值,相当于两次遍历)。所以,我们可以用一次遍历完成分组和求和,然后再用一次遍历求最大值。或者,我们可以使用Collectors.toMap和Collectors.summingInt先得到每个组的总分,再求最大值。因此,第一种方式(先分组求和得到Map<String,Integer>,再求最大值)是高效的,因为分组求和只需要遍历一次,然后求最大值再遍历一次。但是,如果我们想要同时得到分组后的列表和总分,那么第二种方式(分组得到Map<String,List<TeamScore>>)会保留原始数据,但后续需要为每个组计算总和,这样每个组都要遍历一次,如果组内元素多,效率会低。所以,根据需求选择:如果只需要总分最大的组(的teamCode和总分),那么第一种方式更好。如果还需要这个组的所有元素,那么我们可以先分组得到Map<String,List<TeamScore>>,然后计算每个组的总分并记录最大值对应的组。下面我们分别写两个示例:示例1:只需要总分最大的组的teamCode和总分。示例2:需要总分最大的组的所有元素。但是题目没有明确要求,所以我们可以按第一种方式,因为题目说“分组统计”,统计通常指汇总值(如总和)。因此,我们采用第一种方式。代码示例(假设score是整数):```importjava.util.*;importjava.util.stream.Collectors;publicclassDemo{publicstaticvoidmain(String[]args){//示例数据List<TeamScore>teamScores=Arrays.asList(newTeamScore("A",10),newTeamScore("B",20),newTeamScore("A",5),newTeamScore("C",15),newTeamScore("B",25));//按teamCode分组,并计算每个组的总分Map<String,Integer>teamTotalScores=teamScores.stream().collect(Collectors.groupingBy(TeamScore::getTeamCode,Collectors.summingInt(TeamScore::getScore)));//找出总分最大的组Optional<Map.Entry<String,Integer>>maxEntry=teamTotalScores.entrySet().stream().max(Map.Entry.comparingByValue());//输出结果if(maxEntry.isPresent()){System.out.println("总分最大的组:teamCode="+maxEntry.get().getKey()+",总分="+maxEntry.get().getValue());}else{System.out.println("没有数据");}}staticclassTeamScore{privateStringteamCode;privateintscore;publicTeamScore(StringteamCode,intscore){this.teamCode=teamCode;this.score=score;}publicStringgetTeamCode(){returnteamCode;}publicintgetScore(){returnscore;}}}```输出:总分最大的组:teamCode=B,总分=45注意:如果分数是double,则使用Collectors.summingDouble。另外,如果分数是long,则使用Collectors.summingLong。如果需求是求每个组的平均分、最大值等,只需要将Collectors.summingInt换成Collectors.averagingInt、Collectors.maxBy等。但是题目要求是“分数最大的组”,这里我们理解为总分最大。如果是平均分最大,则使用Collectors.averagingDouble等。所以,根据具体需求调整。因此,回答用户的问题,我们可以给出按teamCode分组求总分的示例,然后找到总分最大的组。如果用户的需求是平均分最大,则修改为:Map<String,Double>teamAvgScores=teamScores.stream().collect(Collectors.groupingBy(TeamScore::getTeamCode,Collectors.averagingDouble(TeamScore::getScore)));然后同样使用maxEntry找到平均分最大的组。所以,核心是分组收集器的选择。总结:使用Collectors.groupingBy和适当的下游收集器(如summingInt,averagingDouble等)进行分组统计,然后使用Stream的max方法找到最大值。</think>在 Java JDK8 中使用 Stream API 的 `Collectors.groupingBy` 按 `teamCode` 分组统计并找出分数最大组的完整示例: ```java import java.util.*; import java.util.stream.Collectors; public class TeamScoreAnalysis { // 数据对象 static class TeamScore { private String teamCode; private int score; public TeamScore(String teamCode, int score) { this.teamCode = teamCode; this.score = score; } public String getTeamCode() { return teamCode; } public int getScore() { return score; } } public static void main(String[] args) { // 示例数据 List<TeamScore> scores = Arrays.asList( new TeamScore("A", 80), new TeamScore("B", 200), new TeamScore("A", 150), new TeamScore("C", 100), new TeamScore("B", 300) ); // 1. 分组并计算总分 Map<String, Integer> teamTotalScores = scores.stream() .collect(Collectors.groupingBy( TeamScore::getTeamCode, Collectors.summingInt(TeamScore::getScore) )); // 2. 找出总分最大的组 Optional<Map.Entry<String, Integer>> maxTeam = teamTotalScores.entrySet().stream() .max(Map.Entry.comparingByValue()); // 3. 输出结果 if (maxTeam.isPresent()) { Map.Entry<String, Integer> result = maxTeam.get(); System.out.println("最高分团队: " + result.getKey()); System.out.println("团队总分: " + result.getValue()); // 输出明细数据 System.out.println("\n明细数据:"); scores.stream() .filter(ts -> ts.getTeamCode().equals(result.getKey())) .forEach(ts -> System.out.println( "Team: " + ts.getTeamCode() + ", Score: " + ts.getScore())); } else { System.out.println("无数据"); } } } ``` ### 关键步骤解析: 1. **分组统计** ```java Map<String, Integer> teamTotalScores = scores.stream() .collect(Collectors.groupingBy( TeamScore::getTeamCode, Collectors.summingInt(TeamScore::getScore) )); ``` - `groupingBy(TeamScore::getTeamCode)`:按 `teamCode` 分组 - `summingInt(TeamScore::getScore)`:对每组的 `score` 求和 2. **查找最大组** ```java Optional<Map.Entry<String, Integer>> maxTeam = teamTotalScores.entrySet().stream() .max(Map.Entry.comparingByValue()); ``` - 将分组结果转为 `EntrySet` 流 - `Map.Entry.comparingByValue()`:按值(总分)比较 - `max()`:找出最大值 3. **处理结果** ```java maxTeam.ifPresent(result -> { System.out.println("最高分团队: " + result.getKey()); // 其他操作... }); ``` - 使用 `Optional` 安全处理结果 - 可通过 `filter` 获取最大组的原始数据明细 ### 输出示例: ``` 最高分团队: B 团队总分: 500 明细数据: Team: B, Score: 200 Team: B, Score: 300 ``` ### 其他统计场景: ```java // 求平均分 Map<String, Double> avgScores = scores.stream() .collect(Collectors.groupingBy( TeamScore::getTeamCode, Collectors.averagingDouble(TeamScore::getScore) )); // 求最高分 Map<String, Optional<TeamScore>> maxScores = scores.stream() .collect(Collectors.groupingBy( TeamScore::getTeamCode, Collectors.maxBy(Comparator.comparingInt(TeamScore::getScore)) )); // 分组计数 Map<String, Long> teamCounts = scores.stream() .collect(Collectors.groupingBy( TeamScore::getTeamCode, Collectors.counting() )); ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值