Eu tenho uma coleção de BigDecimals (neste exemplo, a LinkedList
) que gostaria de adicionar. É possível usar fluxos para isso?
Notei que a Stream
classe tem vários métodos
Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong
Cada um dos quais tem um sum()
método conveniente . Mas, como sabemos, float
e double
aritmética é quase sempre uma má idéia.
Então, existe uma maneira conveniente de resumir BigDecimals?
Este é o código que tenho até agora.
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 você pode ver, estou resumindo os BigDecimals usando BigDecimal::doubleValue()
, mas isso (como esperado) não é preciso.
Edição pós-resposta para posteridade:
Ambas as respostas foram extremamente úteis. Eu queria acrescentar um pouco: meu cenário da vida real não envolve uma coleção de BigDecimal
s brutos , eles são embrulhados em uma fatura. Mas pude modificar a resposta de Aman Agnihotri para explicar isso usando a map()
função for stream:
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;
}
}
fonte
Invoice::total
vsinvoice -> invoice.total()
.Collectors.summingInt()
, mas as perde porBigDecimal
s. Em vez de escreverreduce(blah blah blah)
que é difícil de ler, seria melhor escrever para o coletor ausenteBigDecimal
e ter.collect(summingBigDecimal())
no final do seu pipeline.Esta postagem já tem uma resposta marcada, mas a resposta não filtra valores nulos. A resposta correta deve impedir valores nulos usando a função Object :: nonNull como predicado.
Isso impede que valores nulos tentem ser somados à medida que reduzimos.
fonte
Você pode resumir os valores de um
BigDecimal
fluxo usando um coletor reutilizável chamado :summingUp
O
Collector
pode ser implementado assim:fonte
Use esta abordagem para somar a lista de BigDecimal:
Essa abordagem mapeia cada BigDecimal apenas como BigDecimal e os reduz somando-os, que são retornados usando o
get()
métodoAqui está outra maneira simples de fazer a mesma soma:
Atualizar
Se eu escrevesse a expressão classe e lambda na pergunta editada, eu a escreveria da seguinte maneira:
fonte
.map(n -> n)
inútil lá? Tambémget()
não é necessário.get()
como ele retorna o valor doOptional
que é retornado pelareduce
chamada. Se alguém quiser trabalhar comOptional
ou apenas imprimir a soma, então sim,get()
não é necessário. Mas imprimir o opcional opcional imprime diretamente aOptional[<Value>]
sintaxe que duvido que o usuário precisaria. Portanto,get()
é necessário obter um valor a partir doOptional
.get
chamada incondicional ! Sevalues
for uma lista vazia, o opcional não conterá nenhum valor e lançará umNoSuchElementException
quandoget
for chamado. Você pode usar em seuvalues.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
lugar.Se você não se importa com uma dependência de terceiros, há uma classe chamada Collectors2 em Eclipse Collections que contém métodos que retornam Collectors para somar e resumir BigDecimal e BigInteger. Esses métodos usam uma Function como parâmetro para que você possa extrair um valor BigDecimal ou BigInteger de um objeto.
Nota: Sou um colaborador das Coleções Eclipse.
fonte