使用Java 8 Stream对List<Banana>按名称分组并求和重量与价格
没问题,这事儿用Java 8+的Stream API就能轻松搞定,正好适配这种分组聚合的场景。我给你写几种实现方式,从兼容Java 8的基础写法到Java 12+的简洁优化版,再拆解开讲清楚每一步:
第一步:定义聚合结果类
首先我们需要一个类来存储分组后的聚合数据,这样返回的结构更清晰,比用Map或数组可读性强太多:
class BananaSummary { private String name; private Long totalWeight; private Long totalPrice; // 构造器 public BananaSummary(String name, Long totalWeight, Long totalPrice) { this.name = name; this.totalWeight = totalWeight; this.totalPrice = totalPrice; } // Getter方法(按需添加) public String getName() { return name; } public Long getTotalWeight() { return totalWeight; } public Long getTotalPrice() { return totalPrice; } // 重写toString方便打印结果 @Override public String toString() { return "BananaSummary{" + "name='" + name + '\'' + ", totalWeight=" + totalWeight + ", totalPrice=" + totalPrice + '}'; } }
方式一:Java 8兼容的直观写法
这种写法分两步:先按名称分组,再对每个分组单独求和,逻辑清晰,新手也能一眼看懂:
import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class BananaGroupingDemo { public static void main(String[] args) { // 初始化你的香蕉集合 List<Banana> bananas = List.of( new Banana("banana1", 10L, 20L), new Banana("banana1", 10L, 20L), new Banana("banana1", 10L, 20L), new Banana("banana2", 20L, 30L), new Banana("banana2", 30L, 30L), new Banana("banana3", 50L, 40L) ); // 1. 先按香蕉名称分组,得到每个名称对应的香蕉列表 Map<String, List<Banana>> groupedBananas = bananas.stream() .collect(Collectors.groupingBy(Banana::getName)); // 2. 遍历分组,对每个组的weight和price分别求和,封装成BananaSummary List<BananaSummary> summaryList = groupedBananas.entrySet().stream() .map(entry -> { String bananaName = entry.getKey(); // 求和总重量 Long totalWeight = entry.getValue().stream() .mapToLong(Banana::getWeight) .sum(); // 求和总价格 Long totalPrice = entry.getValue().stream() .mapToLong(Banana::getPrice) .sum(); return new BananaSummary(bananaName, totalWeight, totalPrice); }) .collect(Collectors.toList()); // 打印结果 summaryList.forEach(System.out::println); } }
方式二:Java 8的流式聚合写法(一步到位)
如果想在分组的同时完成聚合,可以用Collectors.reducing来实现,省去中间的Map存储:
Map<String, BananaSummary> groupedResult = bananas.stream() .collect(Collectors.groupingBy( Banana::getName, // 分组键:香蕉名称 Collectors.reducing( // 初始值:空的聚合对象 new BananaSummary("", 0L, 0L), // 累加逻辑:把当前香蕉的weight和price加到聚合对象上 (summary, banana) -> new BananaSummary( banana.getName(), summary.getTotalWeight() + banana.getWeight(), summary.getTotalPrice() + banana.getPrice() ), // 合并逻辑(多线程并行流时需要,单线程可以忽略) (summary1, summary2) -> new BananaSummary( summary1.getName(), summary1.getTotalWeight() + summary2.getTotalWeight(), summary1.getTotalPrice() + summary2.getTotalPrice() ) ) )); // 转换为List方便查看 List<BananaSummary> summaryList = new ArrayList<>(groupedResult.values());
方式三:Java 12+的简洁优化写法(用Teeing收集器)
Java 12引入的Collectors.teeing可以同时执行两个独立的收集操作,然后合并结果,处理这种多字段求和的场景特别顺手:
List<BananaSummary> summaryList = bananas.stream() .collect(Collectors.groupingBy(Banana::getName)) .entrySet() .stream() .map(entry -> { String name = entry.getKey(); // 同时对weight和price求和,然后合并成BananaSummary return entry.getValue().stream() .collect(Collectors.teeing( Collectors.summingLong(Banana::getWeight), // 第一个收集器:求重量总和 Collectors.summingLong(Banana::getPrice), // 第二个收集器:求价格总和 (totalWeight, totalPrice) -> new BananaSummary(name, totalWeight, totalPrice) // 合并结果 )); }) .collect(Collectors.toList());
最终输出结果
不管用哪种方式,最终都会得到如下聚合结果:
BananaSummary{name='banana1', totalWeight=30, totalPrice=60} BananaSummary{name='banana2', totalWeight=50, totalPrice=60} BananaSummary{name='banana3', totalWeight=50, totalPrice=40}
内容的提问来源于stack exchange,提问作者CR7
相关产品推荐
相关产品推荐

