![](/img/trans.png)
[英]Can I create a list using streams where the critera are to check one list then add an object to another list?
[英]Using streams how can I create a summary object
請假設我具有以下數據結構
public class Payment {
String paymentType;
double price;
double tax;
double total;
public Payment(String paymentType, double price, double tax, double total) {
super();
this.paymentType = paymentType;
this.price = price;
this.tax = tax;
this.total = total;
}
public String getPaymentType() {
return paymentType;
}
public void setPaymentType(String paymentType) {
this.paymentType = paymentType;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getTax() {
return tax;
}
public void setTax(double tax) {
this.tax = tax;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
}
在另一種方法中,我將具有如下類型的集合:
private Payment generateTotal() {
Collection<Payment> allPayments = new ArrayList<>();
allPayments.add(new Payment("Type1", 100.01, 1.12, 101.13));
allPayments.add(new Payment("Type2", 200.01, 2.12, 202.13));
allPayments.add(new Payment("Type3", 300.01, 3.12, 303.13));
allPayments.add(new Payment("Type4", 400.01, 4.12, 404.13));
allPayments.add(new Payment("Type5", 500.01, 5.12, 505.13));
//Generate the total with a stream and return
return null;
}
我想流這些映射到總對象
即
看起來像這樣的付款對象
paymentType = "Total";
price = sum(payment.price);
tax = sum(payment.tax);
total = sum(payment.total);
我知道我可以一次使用mapToDouble一列來執行此操作,但是我想使用reduce或某種方法來使此操作在一個流中發生。
您可以將自己的Collector
實現為Payment
對象:
Payment total =
allPayments.stream()
.collect(Collector. of(
() -> new Payment("Total", 0.0, 0.0, 0.0),
(Payment p1, Payment p2) -> {
p1.setPrice(p1.getPrice() + p2.getPrice());
p1.setTax(p1.getTax() + p2.getTax());
p1.setTotal(p1.getTotal() + p2.getTotal());
},
(Payment p1, Payment p2) -> {
p1.setPrice(p1.getPrice() + p2.getPrice());
p1.setTax(p1.getTax() + p2.getTax());
p1.setTotal(p1.getTotal() + p2.getTotal());
return p1;
}));
沒有理由在更短,更容易閱讀類似內容的地方使用Streams:
Payment sum = new Payment("Total", 0, 0, 0);
allPayments.forEach(p -> {
sum.price += p.price;
sum.tax += p.tax;
sum.total += p.total;
});
正如評論中所討論的,此解決方案不僅更短,更清潔(IMO),而且更易於維護:例如,說現在您有一個例外:您想繼續對所有這些屬性進行求和,但希望排除其中的項目第二個索引。 與簡單的for循環相比,將其添加到reduce-verion有多容易?
有趣的是,該解決方案具有較小的內存占用空間(因為reduce每次迭代都會創建一個額外的對象),並且在提供的示例中可以更高效地運行。
缺點:我唯一能找到的是萬一我們處理的集合很大(數千個或更多),在這種情況下,我們應該對Stream.parallel使用reduce解決方案,但即便如此,也應謹慎進行
通過以下方式在JMH中進行基准測試:
@Benchmark
public Payment loopIt() {
Collection<Payment> allPayments = new ArrayList<>();
allPayments.add(new Payment("Type1", 100.01, 1.12, 101.13));
allPayments.add(new Payment("Type2", 200.01, 2.12, 202.13));
allPayments.add(new Payment("Type3", 300.01, 3.12, 303.13));
allPayments.add(new Payment("Type4", 400.01, 4.12, 404.13));
allPayments.add(new Payment("Type5", 500.01, 5.12, 505.13));
Payment accum = new Payment("Total", 0, 0, 0);
allPayments.forEach(x -> {
accum.price += x.price;
accum.tax += x.tax;
accum.total += x.total;
});
return accum;
}
@Benchmark
public Payment reduceIt() {
Collection<Payment> allPayments = new ArrayList<>();
allPayments.add(new Payment("Type1", 100.01, 1.12, 101.13));
allPayments.add(new Payment("Type2", 200.01, 2.12, 202.13));
allPayments.add(new Payment("Type3", 300.01, 3.12, 303.13));
allPayments.add(new Payment("Type4", 400.01, 4.12, 404.13));
allPayments.add(new Payment("Type5", 500.01, 5.12, 505.13));
return
allPayments.stream()
.reduce(
new Payment("Total", 0, 0, 0),
(sum, each) -> new Payment(
sum.getPaymentType(),
sum.getPrice() + each.getPrice(),
sum.getTax() + each.getTax(),
sum.getTotal() + each.getTotal()));
}
結果:
Result "play.Play.loopIt":
49.838 ±(99.9%) 1.601 ns/op [Average]
(min, avg, max) = (43.581, 49.838, 117.699), stdev = 6.780
CI (99.9%): [48.236, 51.439] (assumes normal distribution)
# Run complete. Total time: 00:07:36
Benchmark Mode Cnt Score Error Units
Play.loopIt avgt 200 49.838 ± 1.601 ns/op
Result "play.Play.reduceIt":
129.960 ±(99.9%) 4.163 ns/op [Average]
(min, avg, max) = (109.616, 129.960, 212.410), stdev = 17.626
CI (99.9%): [125.797, 134.123] (assumes normal distribution)
# Run complete. Total time: 00:07:36
Benchmark Mode Cnt Score Error Units
Play.reduceIt avgt 200 129.960 ± 4.163 ns/op
我不會為此使用流,但是自從您問到:
Payment total =
allPayments.stream()
.reduce(
new Payment("Total", 0, 0, 0),
(sum, each) -> new Payment(
sum.getPaymentType(),
sum.getPrice() + each.getPrice(),
sum.getTax() + each.getTax(),
sum.getTotal() + each.getTotal()));
您需要一個BinaryOperator<Payment> accumulator
來組合兩個Payment
:
public static Payment reduce(Payment p1, Payment p2) {
return new Payment("Total",
p1.getPrice() + p2.getPrice(),
p1.getTax() + p2.getTax(),
p1.getTotal() + p2.getTotal()
);
}
減少將如下所示:
Payment payment = allPayments.stream().reduce(new Payment(), Payment::reduce);
或(避免創建身份對象):
Optional<Payment> oPayment = allPayments.stream().reduce(Payment::reduce);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.