使用stream添加BigDecimals

我有一个BigDecimals集合(在这个例子中是一个LinkedList ),我想将它们加在一起。 有没有可能为此使用stream?

我注意到Stream类有几个方法

 Stream::mapToInt Stream::mapToDouble Stream::mapToLong 

每个方法都有一个方便的sum()方法。 但是,正如我们所知, floatdouble算术几乎总是一个坏主意。

那么,总结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编辑后人:

两个答案都非常有帮助。 我想补充一点:我的真实生活场景不涉及原始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; } } 

原始答案

是的,这是可能的:

 List<BigDecimal> bdList = new ArrayList<>(); //populate list BigDecimal result = bdList.stream() .reduce(BigDecimal.ZERO, BigDecimal::add); 

它所做的是:

  1. 获取一个List<BigDecimal>
  2. 把它变成一个Stream<BigDecimal>
  3. 调用reduce方法。

    3.1。 我们提供了一个用于添加的身份值,即BigDecimal.ZERO

    3.2。 我们指定BinaryOperator<BigDecimal> ,它通过方法引用BigDecimal::add添加两个BigDecimal

编辑后更新答案

我看到你已经添加了新的数据,因此新的答案将变成:

 List<Invoice> invoiceList = new ArrayList<>(); //populate Function<Invoice, BigDecimal> totalMapper = invoice -> invoice.getUnit_price().multiply(invoice.getQuantity()); BigDecimal result = invoiceList.stream() .map(totalMapper) .reduce(BigDecimal.ZERO, BigDecimal::add); 

除了我已经添加了一个totalMappervariables,它具有从InvoiceBigDecimal的函数并返回该Invoice的总价格之外,它基本上是相同的。

然后,我获得一个Stream<Invoice> ,将其映射到一个Stream<BigDecimal> ,然后将其减less为一个BigDecimal

现在,从OOPdevise的angular度来说,我build议你也使用已经定义的total()方法,那么它就变得更简单了:

 List<Invoice> invoiceList = new ArrayList<>(); //populate BigDecimal result = invoiceList.stream() .map(Invoice::total) .reduce(BigDecimal.ZERO, BigDecimal::add); 

这里我们直接使用map方法中的方法引用。

使用这种方法来总结BigDecimal的列表:

 List<BigDecimal> values = ... // List of BigDecimal objects BigDecimal sum = values.stream().reduce((x, y) -> x.add(y)).get(); 

此方法仅将每个BigDecimal映射为BigDecimal,然后将它们相加,然后使用get()方法返回。

这是另一个简单的方法来做相同的总结:

 List<BigDecimal> values = ... // List of BigDecimal objects BigDecimal sum = values.stream().reduce(BigDecimal::add).get(); 

更新

如果我要在编辑的问题中编写类和lambdaexpression式,我会写下如下:

 import java.math.BigDecimal; import java.util.LinkedList; public class Demo { 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))); // Java 8 approach, using Method Reference for mapping purposes. invoices.stream().map(Invoice::total).forEach(System.out::println); System.out.println("Sum = " + invoices.stream().map(Invoice::total).reduce((x, y) -> x.add(y)).get()); } // This is just my style of writing classes. Yours can differ. static class Invoice { private String company; private String number; private BigDecimal unitPrice; private BigDecimal quantity; public Invoice() { unitPrice = quantity = BigDecimal.ZERO; } public Invoice(String company, String number, BigDecimal unitPrice, BigDecimal quantity) { setCompany(company); setNumber(number); setUnitPrice(unitPrice); setQuantity(quantity); } public BigDecimal total() { return unitPrice.multiply(quantity); } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public BigDecimal getUnitPrice() { return unitPrice; } public void setUnitPrice(BigDecimal unitPrice) { this.unitPrice = unitPrice; } public BigDecimal getQuantity() { return quantity; } public void setQuantity(BigDecimal quantity) { this.quantity = quantity; } } } 

您可以使用名为summingUp的收集器来总结BigDecimalstream的值:

 BigDecimal sum = bigDecimalStream.collect(summingUp()); 

Collector可以这样实现:

 public static Collector<BigDecimal, ?, BigDecimal> summingUp() { return Collectors.reducing(BigDecimal.ZERO, BigDecimal::add); } 

我想出了以下的收集器实现来解决这个问题。 它是用Groovy编写的,所以如果您只使用Java,则可能需要修改它,但如果BigDecimal的ctor支持这些types,则它可以支持任意types的stream:

 public static <T> Collector<T, ?, BigDecimal> summingBigDecimal() { new java.util.stream.Collectors.CollectorImpl<?, ?, BigDecimal>( { [BigDecimal.ZERO].toArray(new BigDecimal[1]) }, { BigDecimal[] a, Object t -> a[0] = (t instanceof BigDecimal ? a[0].add(t) : a[0].add(new BigDecimal(t))) }, { BigDecimal[] a, BigDecimal[] b -> a[0].add(b[0]) }, { BigDecimal[] a -> a[0] }, Collections.emptySet()); } 

我相信它可以清理一下,但能够做到这样的事情:

 Stream.of("1", 3L, new BigDecimal("5")).collect(Collectors.summingBigDecimal()) 

在某些情况下,我已经certificate是有用的,因为我不想被困扰于自己做types转换。