Averaging across multiple fields with IntSummaryStatistics

前端 未结 2 639
无人共我
无人共我 2021-01-07 01:07

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;

相关标签:
2条回答
  • 2021-01-07 01:19

    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.

    0 讨论(0)
  • 2021-01-07 01:29

    You need to write your own Collector, something like this:

    class CarDataAverage {
        public static Collector<CarData, CarDataAverage, Optional<CarData>> get() {
            return Collector.of(CarDataAverage::new, CarDataAverage::add,
                                CarDataAverage::combine,CarDataAverage::finish);
        }
        private long sumBodyWeight;
        private long sumShellWeight;
        private int count;
        private void add(CarData carData) {
            this.sumBodyWeight += carData.getBodyWeight();
            this.sumShellWeight += carData.getShellWeight();
            this.count++;
        }
        private CarDataAverage combine(CarDataAverage that) {
            this.sumBodyWeight += that.sumBodyWeight;
            this.sumShellWeight += that.sumShellWeight;
            this.count += that.count;
            return this;
        }
        private Optional<CarData> finish() {
            if (this.count == 0)
                return Optional.empty();
            // adjust as needed if averages should be rounded
            return Optional.of(new CarData((int) (this.sumBodyWeight / this.count),
                                           (int) (this.sumShellWeight / this.count)));
        }
    }
    

    You then use it like this:

    List<CarData> list = ...
    
    Optional<CarData> averageCarData = list.stream().collect(CarDataAverage.get());
    
    0 讨论(0)
提交回复
热议问题