使用IntSummaryStatistics对多个字段进行平均。

huangapple go评论80阅读模式
英文:

Averaging across multiple fields with IntSummaryStatistics

问题

CarData = new CarData();
CarData.getBodyWeight 返回 Integer
CarData.getShellWeight 返回 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();

averageShellWeight.getAverage();

我不想把每个字段都放回到最终的返回结果中

在视觉上这是我的列表

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

我想要实现的输出是一个单一的对象其中包含我指定字段的平均值不确定是否需要使用 `Collectors.averagingInt` 或者一些组合的 IntSummaryStatistics 来实现这一点在这些技术中针对一个字段很容易实现只是不确定在使用多个整数字段时漏掉了什么

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

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&lt;CarData&gt; carData = carResults.getCars();
   	
	IntSummaryStatistics averageBodyWeight = carData.stream()
            .mapToInt((x) -&gt; x.getBodyWeight())
            .summaryStatistics();
   	
	averageBodyWeight.getAverage(); 


    		IntSummaryStatistics averageShellWeight = carData.stream()
            .mapToInt((x) -&gt; 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 } }

答案1

得分: 6

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.

英文:

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) -&gt; 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.

答案2

得分: 4

你需要编写自己的Collector,类似于以下示例:

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();
        // 根据需要进行舍入以计算平均值
        return Optional.of(new CarData((int) (this.sumBodyWeight / this.count),
                                       (int) (this.sumShellWeight / this.count)));
    }
}

然后可以像这样使用它:

List<CarData> list = ...

Optional<CarData> averageCarData = list.stream().collect(CarDataAverage.get());
英文:

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

class CarDataAverage {
	public static Collector&lt;CarData, CarDataAverage, Optional&lt;CarData&gt;&gt; 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&lt;CarData&gt; 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&lt;CarData&gt; list = ...

Optional&lt;CarData&gt; averageCarData = list.stream().collect(CarDataAverage.get());

huangapple
  • 本文由 发表于 2020年9月9日 18:15:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/63809505.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定