Tengo una colección de BigDecimals (en este ejemplo, a LinkedList
) que me gustaría agregar. ¿Es posible usar transmisiones para esto?
Noté que la Stream
clase tiene varios métodos
Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong
Cada uno de los cuales tiene un sum()
método conveniente . Pero, como sabemos, float
y la double
aritmética es casi siempre una mala idea.
Entonces, ¿hay una manera conveniente de resumir BigDecimals?
Este es el código que tengo hasta ahora.
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());
}
Como puede ver, estoy resumiendo los BigDecimals usando BigDecimal::doubleValue()
, pero esto (como se esperaba) no es preciso.
Edición posterior a la respuesta para la posteridad:
Ambas respuestas fueron extremadamente útiles. Quería agregar un poco: mi escenario de la vida real no involucra una colección de BigDecimal
s sin procesar , están envueltos en una factura. Pero pude modificar la respuesta de Aman Agnihotri para dar cuenta de esto usando la map()
función de transmisión:
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;
}
}
fuente
Invoice::total
vsinvoice -> invoice.total()
.Collectors.summingInt()
, pero las pierde porBigDecimal
s. En lugar de escribirreduce(blah blah blah)
que es difícil de leer, sería mejor escribir el recopilador perdidoBigDecimal
y tenerlo.collect(summingBigDecimal())
al final de su canalización.Esta publicación ya tiene una respuesta marcada, pero la respuesta no filtra los valores nulos. La respuesta correcta debería evitar valores nulos mediante el uso de la función Object :: nonNull como predicado.
Esto evita que los valores nulos intenten sumarse a medida que reducimos.
fuente
Puede resumir los valores de una
BigDecimal
secuencia utilizando un recopilador reutilizable denominado :summingUp
El
Collector
puede implementarse así:fuente
Use este enfoque para sumar la lista de BigDecimal:
Este enfoque asigna cada BigDecimal solo como BigDecimal y los reduce al sumarlos, que luego se devuelven utilizando el
get()
método.Aquí hay otra forma simple de hacer la misma suma:
Actualizar
Si tuviera que escribir la clase y la expresión lambda en la pregunta editada, la habría escrito de la siguiente manera:
fuente
.map(n -> n)
inútil allí? Tampocoget()
es necesario.get()
ya que devuelve el valor delOptional
que devuelve lareduce
llamada. Si uno quiere trabajar con elOptional
o simplemente imprimir la suma, entonces sí,get()
no es necesario. Pero imprimir el Opcional imprime directamente laOptional[<Value>]
sintaxis basada que dudo que el usuario necesite. Porget()
lo tanto, es necesario para obtener el valor deOptional
.get
llamada incondicional ! Sivalues
es una lista vacía, lo opcional no contendrá ningún valor y arrojará unNoSuchElementException
cuándoget
se llama. Puedes usarvalues.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
en su lugar.Si no le importa una dependencia de terceros, hay una clase llamada Collectors2 en Eclipse Collections que contiene métodos que devuelven Collectors para sumar y resumir BigDecimal y BigInteger. Estos métodos toman una función como parámetro para que pueda extraer un valor BigDecimal o BigInteger de un objeto.
Nota: Soy un committer para Eclipse Collections.
fuente