Java流式处理:按Cause名称分组汇总amount并生成Top3统计结果
问题修正:按Cause名称分组统计并生成Top3汇总字符串
需求说明
遍历Cause对象列表,按name字段分组:
- 统计每个
name的出现次数 - 汇总该组内所有对象的
float类型amount值 - 取出现次数最多的前3组
- 将每组的名称、出现次数、amount总和格式化为指定字符串返回
当前代码问题
- 计算金额总和时,误用
Cause::getAmount作为分组键,而非正确的Cause::getName - 重复对
sources执行流式分组操作,存在冗余,降低性能 - 拆分结果到多个List后通过索引拼接,存在索引越界风险(虽捕获异常但属于不良实现)
原错误代码片段
List<Float> totalAmounts = sources.stream().collect(Collectors.groupingBy(Cause::getAmount, Collectors.counting())) .entrySet().stream().sorted(entryComp).map(Map.Entry::getValue).limit(3).toList();
Cause类结构(含Getter/Setter)
public Cause(String name, int time, float amount) {}
示例数据源
new Cause("Name", 34, 15.53F); new Cause("Name", 636, 2.12F); new Cause("Name", 2345, 3.14F); new Cause("Name", 568, 9F); new Cause("OtherName", 10, 5.55F); new Cause("OtherName", 5324, 0.27F); new Cause("OtherName", 1, 6.21F); new Cause("RealName", 921, 7.05F); new Cause("RealName", 3899, 11.11F); new Cause("RealName", 1782, 8.38F); new Cause("BadName", 234, 1.01F); new Cause("BadName", 581, 6.31F);
预期分组统计结果
每组对应(出现次数,amount总和):
(4, 29.79F) // 对应Name (3, 12.03F) // 对应OtherName (3, 26.54F) // 对应RealName
待修正完整代码
List<Cause> sources = new CopyOnWriteArrayList<>(); private String topThree() { StringBuilder result = new StringBuilder(); Comparator<Map.Entry<String, Long>> entryComparator = Collections.reverseOrder(Map.Entry.comparingByValue()); Comparator<Map.Entry<Float, Double>> entryComp = Collections.reverseOrder(Map.Entry.comparingByValue()); List<String> most = sources.stream().collect(Collectors.groupingBy(Cause::getName, Collectors.counting())) .entrySet().stream().sorted(entryComparator).map(Map.Entry::getKey).limit(3).toList(); List<Long> mostTimes = sources.stream().collect(Collectors.groupingBy(Cause::getName, Collectors.counting())) .entrySet().stream().sorted(entryComparator).map(Map.Entry::getValue).limit(3).toList(); List<Float> total = sources.stream().collect(Collectors.groupingBy(Cause::getAmount, Collectors.counting())) .entrySet().stream().sorted(entryComp).map(Map.Entry::getValue).limit(3).toList(); for (int i = 0; i < most.size(); i++) { try { result.append((i+1) + ". " + most.get(i) + " " + mostTimes.get(i) + " " + total.get(i) + "\n"); } catch (ArrayIndexOutOfBoundsException ignored) {} } return result.toString(); } @Getter @Setter private static final class Cause { private Source source; private String name; private float amount; private int tickRecordedOn; public Cause(Source source, String name, float amount, int tickRecordedOn) { this.source = source; this.name = name; this.amount = amount; this.tickRecordedOn = tickRecordedOn;// Used for syncing, nothing else } }
修正后的代码
List<Cause> sources = new CopyOnWriteArrayList<>(); private String topThree() { StringBuilder result = new StringBuilder(); // 一次性分组,同时统计每个name的出现次数和amount总和 Map<String, Map.Entry<Long, Float>> groupStats = sources.stream() .collect(Collectors.groupingBy( Cause::getName, Collectors.teeing( Collectors.counting(), Collectors.summingDouble(Cause::getAmount), (count, sum) -> new AbstractMap.SimpleEntry<>(count, (float) sum) ) )); // 按出现次数倒序排序,取前3组并拼接结果 AtomicInteger rank = new AtomicInteger(1); groupStats.entrySet().stream() .sorted((e1, e2) -> Long.compare(e2.getValue().getKey(), e1.getValue().getKey())) .limit(3) .forEach(entry -> { result.append(String.format("%d. %s %d %.2f%n", rank.getAndIncrement(), entry.getKey(), entry.getValue().getKey(), entry.getValue().getValue() )); }); return result.toString().trim(); } @Getter @Setter private static final class Cause { private Source source; private String name; private float amount; private int tickRecordedOn; public Cause(Source source, String name, float amount, int tickRecordedOn) { this.source = source; this.name = name; this.amount = amount; this.tickRecordedOn = tickRecordedOn;// Used for syncing, nothing else } }
修正说明
- 消除冗余流式处理:通过
Collectors.teeing(Java 12+支持)一次性完成分组、次数统计和金额总和计算,避免多次遍历数据源 - 修正分组键错误:统一使用
Cause::getName作为分组依据,不再误用getAmount - 简化结果拼接:直接在排序后的流中完成字符串拼接,避免拆分多个List导致的索引越界问题
- 格式化输出:使用
String.format保证输出格式统一,符合要求的(Cause Name) (出现次数) (amount总和)格式
期望返回格式示例
1. Name 4 29.79 2. OtherName 3 12.03 3. RealName 3 26.54
内容的提问来源于stack exchange,提问作者Extent
相关产品推荐
相关产品推荐

