我有一个BigDecimals的集合(在这个例子中,一个 LinkedList
),我想加在一起 . 是否可以使用流?
我注意到 Stream
类有几种方法
Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong
每个方法都有一个方便的 sum()
方法 . 但是,正如我们所知, float
和 double
算术几乎总是一个坏主意 .
那么,有没有一种方便的方法来总结BigDecimals?
这是我到目前为止的代码 .
public static void main(String[] args) {
LinkedList<BigDecimal> values = new LinkedList<>();
values.add(BigDecimal.valueOf(.1));
values.add(BigDecimal.valueOf(1.1));
values.add(BigDecimal.valueOf(2.1));
values.add(BigDecimal.valueOf(.1));
// Classical Java approach
BigDecimal sum = BigDecimal.ZERO;
for(BigDecimal value : values) {
System.out.println(value);
sum = sum.add(value);
}
System.out.println("Sum = " + sum);
// Java 8 approach
values.forEach((value) -> System.out.println(value));
System.out.println("Sum = " + values.stream().mapToDouble(BigDecimal::doubleValue).sum());
System.out.println(values.stream().mapToDouble(BigDecimal::doubleValue).summaryStatistics().toString());
}
正如你所看到的,我正在使用 BigDecimal::doubleValue()
总结BigDecimals,但这(正如预期的那样)并不准确 .
Post-answer edit for posterity:
这两个答案都非常有帮助 . 我想补充一点:我的现实场景不涉及原始 BigDecimal
的集合,它们包含在发票中 . 但是,通过使用流的 map()
函数,我能够修改Aman Agnihotri的答案以解决这个问题:
public static void main(String[] args) {
LinkedList<Invoice> invoices = new LinkedList<>();
invoices.add(new Invoice("C1", "I-001", BigDecimal.valueOf(.1), BigDecimal.valueOf(10)));
invoices.add(new Invoice("C2", "I-002", BigDecimal.valueOf(.7), BigDecimal.valueOf(13)));
invoices.add(new Invoice("C3", "I-003", BigDecimal.valueOf(2.3), BigDecimal.valueOf(8)));
invoices.add(new Invoice("C4", "I-004", BigDecimal.valueOf(1.2), BigDecimal.valueOf(7)));
// Classical Java approach
BigDecimal sum = BigDecimal.ZERO;
for(Invoice invoice : invoices) {
BigDecimal total = invoice.unit_price.multiply(invoice.quantity);
System.out.println(total);
sum = sum.add(total);
}
System.out.println("Sum = " + sum);
// Java 8 approach
invoices.forEach((invoice) -> System.out.println(invoice.total()));
System.out.println("Sum = " + invoices.stream().map((x) -> x.total()).reduce((x, y) -> x.add(y)).get());
}
static class Invoice {
String company;
String invoice_number;
BigDecimal unit_price;
BigDecimal quantity;
public Invoice() {
unit_price = BigDecimal.ZERO;
quantity = BigDecimal.ZERO;
}
public Invoice(String company, String invoice_number, BigDecimal unit_price, BigDecimal quantity) {
this.company = company;
this.invoice_number = invoice_number;
this.unit_price = unit_price;
this.quantity = quantity;
}
public BigDecimal total() {
return unit_price.multiply(quantity);
}
public void setUnit_price(BigDecimal unit_price) {
this.unit_price = unit_price;
}
public void setQuantity(BigDecimal quantity) {
this.quantity = quantity;
}
public void setInvoice_number(String invoice_number) {
this.invoice_number = invoice_number;
}
public void setCompany(String company) {
this.company = company;
}
public BigDecimal getUnit_price() {
return unit_price;
}
public BigDecimal getQuantity() {
return quantity;
}
public String getInvoice_number() {
return invoice_number;
}
public String getCompany() {
return company;
}
}
5 回答
原始答案
是的,这是可能的:
它的作用是:
获得
List<BigDecimal>
.把它变成
Stream<BigDecimal>
调用reduce方法 .
3.1 . 我们提供添加的标识值,即
BigDecimal.ZERO
.3.2 . 我们指定
BinaryOperator<BigDecimal>
,它通过方法引用BigDecimal::add
添加两个BigDecimal
.编辑后更新了答案
我看到你添加了新数据,因此新答案将变为:
它大致相同,只是我添加了
totalMapper
变量,其函数从Invoice
到BigDecimal
并返回该发票的总价 .然后我获得
Stream<Invoice>
,将其映射到Stream<BigDecimal>
然后将其缩小为BigDecimal
.现在,从OOP设计的角度来看,我建议您实际使用已经定义的
total()
方法,然后它变得更容易:这里我们直接使用
map
方法中的方法引用 .使用此方法对BigDecimal列表求和:
此方法仅将每个BigDecimal映射为BigDecimal,并通过对它们求和来减少它们,然后使用
get()
方法返回它们 .这是进行相同求和的另一种简单方法:
Update
如果我在编辑的问题中编写类和lambda表达式,我会写如下:
如果您不介意第三方依赖项,则Eclipse Collections中有一个名为Collectors2的类,其中包含为summing和summarizing BigDecimal和BigInteger返回收集器的方法 . 这些方法将Function作为参数,因此您可以从对象中提取BigDecimal或BigInteger值 .
注意:我是Eclipse Collections的提交者 .
您可以使用名为
summingUp
的 reusable Collector来汇总BigDecimal
流的值:Collector
可以像这样实现:这篇文章已经有一个检查答案,但答案不会过滤空值 . 正确的答案应该通过使用Object :: nonNull函数作为谓词来阻止空值 .
这可以防止空值在我们减少时尝试求和 .