Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Averaging across multiple fields with IntSummaryStatistics

I'm trying to use Java 8 streams to create a single CarData object, which consists of an average of all the CarData fields in the list coming from 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(); 

I don't want to have to put each of these back together in my final returned result.

Visually, this is my list

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

and the output I'm trying to achieve is a single object that has the average of each of the fields I specify. not sure If I need to use Collectors.averagingInt or some combo of IntSummaryStatistics to achieve this. Easy to do across one field for either of these techniques, just not sure what I'm missing when using multiple integer fields.

 {CarData: { getBodyWeight=8, getShellWeight=9 } }
like image 456
Squiggs. Avatar asked Dec 05 '25 06:12

Squiggs.


1 Answers

Starting with JDK 12, you can use the following solution:

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

For older Java versions, you can do either, add the teeing implementation of this answer to your code base and use it exactly as above or create a custom collector tailored to your task, as shown in Andreas’ answer.

Or consider that streaming twice over a List in memory is not necessarily worse than doing two operations in one stream, both, readability- and performance-wise.

Note that calling intValue() on Double objects has the same behavior as the (int) casts in Andreas’ answer. So in either case, you have to adjust the code if other rounding behavior is intended.

Or you consider using a different result object, capable of holding two floating point values for the averages.

like image 61
Holger Avatar answered Dec 07 '25 19:12

Holger