使用stream添加BigDecimals
我有一个BigDecimals集合(在这个例子中是一个LinkedList
),我想将它们加在一起。 有没有可能为此使用stream?
我注意到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编辑后人:
两个答案都非常有帮助。 我想补充一点:我的真实生活场景不涉及原始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);
它所做的是:
- 获取一个
List<BigDecimal>
。 - 把它变成一个
Stream<BigDecimal>
-
调用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);
除了我已经添加了一个totalMapper
variables,它具有从Invoice
到BigDecimal
的函数并返回该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
的收集器来总结BigDecimal
stream的值:
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转换。