首页 > 解决方案 > 使用 IntSummaryStatistics 跨多个字段求平均值

问题描述

我正在尝试使用 Java 8 流来创建单个 CarData 对象,该对象由来自列表中的所有 CarData 字段的平均值组成getCars

   CarData = new CarData();
   CarData.getBodyWeight returns Integer
   CarData.getShellWeight returns Integer

     List<CarData> carData = carResults.getCars();
    
    IntSummaryStatistics averageBodyWeight = carData.stream()
            .mapToInt((x) -> x.getBodyWeight())
            .summaryStatistics();
    
    averageBodyWeight.getAverage(); 


            IntSummaryStatistics averageShellWeight = carData.stream()
            .mapToInt((x) -> x.getShellWeight())
            .summaryStatistics();
    
    getShellWeight.getAverage(); 

我不想在我最终返回的结果中将这些重新组合在一起。

从视觉上看,这是我的清单

getCars() : [
 {CarData: { getBodyWeight=10, getShellWeight=3 } }
 {CarData: { getBodyWeight=6, getShellWeight=5 } }
 {CarData: { getBodyWeight=8, getShellWeight=19 } }
]

我试图实现的输出是一个对象,它具有我指定的每个字段的平均值。不确定我是否需要使用Collectors.averagingIntIntSummaryStatistics 或一些组合来实现这一点。对于这些技术中的任何一种,跨一个字段都很容易做到,只是不确定在使用多个整数字段时我缺少什么。

 {CarData: { getBodyWeight=8, getShellWeight=9 } }

标签: javajava-8java-stream

解决方案


从 JDK 12 开始,您可以使用以下解决方案:

CarData average = carData.stream().collect(Collectors.teeing(
    Collectors.averagingInt(CarData::getBodyWeight),
    Collectors.averagingInt(CarData::getShellWeight),
    (avgBody, avgShell) -> new CarData(avgBody.intValue(), avgShell.intValue())));

对于较旧的 Java 版本,您可以这样做,将这个答案teeing的实现添加到您的代码库并完全按照上面的方式使用它,或者创建一个为您的任务量身定制的自定义收集器,如Andreas 的答案所示。

或者考虑List在内存中进行两次流式传输并不一定比在一个流中执行两个操作更糟糕,无论是可读性还是性能方面。

请注意,调用对象intValue()Double行为与(int)Andreas 回答中的强制转换相同。因此,无论哪种情况,如果要进行其他舍入行为,您都必须调整代码。

或者您考虑使用不同的结果对象,能够为平均值保存两个浮点值。


推荐阅读