【发布时间】:2014-12-31 11:38:49
【问题描述】:
我有一个像这样的“请求”对象(id、数量、价格)的列表
List<Request> requests = Arrays.asList(
new Request(id++, 20, 59.28),
new Request(id++, 10, 61.23),
new Request(id++, 30, 60.67),
new Request(id++, 25, 60.16),
new Request(id++, 60, 59.67));
我想在一次迭代中计算两个指标 - sum(amount) 和 sum(amount * price)。我需要他们计算平均价格:sum(amount * price) / sum(amount)。
考虑到我想使用 Java 8 流,我发现的唯一变体是将值映射到 Pair 对象并实现自定义使用者:
static class Aggregate implements Consumer<Pair<Double, Double>> {
private double count = 0L;
private double sum = 0L;
public double average() {
return count > 0 ? sum/(double) count : 0;
}
public void combine(Aggregate other) {
count += other.count;
sum += other.sum;
}
@Override
public void accept(Pair<Double, Double> data) {
this.count += data.getLeft();
this.sum += data.getLeft() * data.getRight();
}
}
Double avgPrice = requests.stream()
.map(e -> Pair.<Double, Double>of(e.getAmount(), e.getPrice()))
.collect(Aggregate::new, Aggregate::accept, Aggregate::combine)
.average();
这种方法看起来很混乱 - 我们必须为每个条目创建额外的 Pair 对象:(
有人知道更好的解决方案吗?
【问题讨论】:
-
只迭代一次对您来说真的很重要吗?这听起来像是“简单与效率”的平衡之一……您是否有证据表明,如果您分两遍做会太慢?
-
为什么您的 Aggregate 接受 Pair 实例,而不是简单地接受 Request 实例?将请求转换为对有什么意义?
-
@JBNizet 你是对的,我的错。
-
还要注意
sum应该是double,而不是long。 -
因为这样代码最终会变得更简单......就这么简单。绝对最有效的做事方式很少是最简单的做事方式。
标签: java stream aggregate-functions