民间,
考虑以下示例,给定一个Trade对象列表,我的代码需要返回一个包含24小时,7天,30天和所有时间交易量的数组。
使用普通的旧迭代器,这只需要对集合进行一次迭代。
我尝试使用Java 8流和Lambda表达式来做同样的事情。我提出了这个代码,它看起来很优雅,工作正常,但需要在列表上进行4次迭代:
public static final int DAY = 24 * 60 * 60;
public double[] getTradeVolumes(List<Trade> trades, int timeStamp) {
double volume = trades.stream().mapToDouble(Trade::getVolume).sum();
double volume30d = trades.stream().filter(trade -> trade.getTimestamp() + 30 * DAY > timeStamp).mapToDouble(Trade::getVolume).sum();
double volume7d = trades.stream().filter(trade -> trade.getTimestamp() + 7 * DAY > timeStamp).mapToDouble(Trade::getVolume).sum();
double volume24h = trades.stream().filter(trade -> trade.getTimestamp() + DAY > timeStamp).mapToDouble(Trade::getVolume).sum();
return new double[]{volume24h, volume7d, volume30d, volume};
}
如何在列表中仅使用一次迭代来实现相同的目标?
答案 0 :(得分:9)
此问题类似于“摘要统计信息”收集器。看一下IntSummaryStatistics
课程:
public class IntSummaryStatistics implements IntConsumer {
private long count;
private long sum;
...
public void accept(int value) {
++count;
sum += value;
min = Math.min(min, value);
max = Math.max(max, value);
}
...
}
它旨在与collect()
一起使用;这是IntStream.summaryStatistics()
public final IntSummaryStatistics summaryStatistics() {
return collect(IntSummaryStatistics::new, IntSummaryStatistics::accept,
IntSummaryStatistics::combine);
}
写这样的Collector
的好处是你的自定义聚合可以并行运行。
答案 1 :(得分:1)
感谢Brian,我最终实现了下面的代码,它并不像我希望的那样简单,但至少它只迭代一次,它的并行准备就绪,它通过我的单元测试。 欢迎任何改进的想法。
public double[] getTradeVolumes(List<Trade> trades, int timeStamp) {
TradeVolume tradeVolume = trades.stream().collect(
() -> new TradeVolume(timeStamp),
TradeVolume::accept,
TradeVolume::combine);
return tradeVolume.getVolume();
}
public static final int DAY = 24 * 60 * 60;
static class TradeVolume {
private int timeStamp;
private double[] volume = new double[4];
TradeVolume(int timeStamp) {
this.timeStamp = timeStamp;
}
public void accept(Trade trade) {
long tradeTime = trade.getTimestamp();
double tradeVolume = trade.getVolume();
volume[3] += tradeVolume;
if (!(tradeTime + 30 * DAY > timeStamp)) {
return;
}
volume[2] += tradeVolume;
if (!(tradeTime + 7 * DAY > timeStamp)) {
return;
}
volume[1] += tradeVolume;
if (!(tradeTime + DAY > timeStamp)) {
return;
}
volume[0] += tradeVolume;
}
public void combine(TradeVolume tradeVolume) {
volume[0] += tradeVolume.volume[0];
volume[1] += tradeVolume.volume[1];
volume[2] += tradeVolume.volume[2];
volume[3] += tradeVolume.volume[3];
}
public double[] getVolume() {
return volume;
}
}
答案 2 :(得分:0)
有可能使用Collectors.groupingBy
方法对数据进行分区,但方程式会很复杂而且没有意图揭示。
由于getTimestamp()
是一项昂贵的操作,因此最好将其保留为Java 8之前的迭代,因此您只需按Trade
计算一次值。
仅仅因为Java 8添加了闪亮的新工具,不要试图把它变成锤子来锤击所有钉子。