J'ai une collection de BigDecimals (dans cet exemple, a LinkedList
) que je voudrais ajouter ensemble. Est-il possible d'utiliser des flux pour cela?
J'ai remarqué que la Stream
classe a plusieurs méthodes
Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong
Chacun d'eux a une sum()
méthode pratique . Mais, comme nous le savons, float
et l' double
arithmétique est presque toujours une mauvaise idée.
Alors, y a-t-il un moyen pratique de résumer BigDecimals?
C'est le code que j'ai jusqu'à présent.
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());
}
Comme vous pouvez le voir, je résume les BigDecimals en utilisant BigDecimal::doubleValue()
, mais ce n'est (comme prévu) pas précis.
Modification post-réponse pour la postérité:
Les deux réponses ont été extrêmement utiles. Je voulais ajouter un peu: mon scénario réel n'implique pas une collection de bruts BigDecimal
, ils sont emballés dans une facture. Mais, j'ai pu modifier la réponse d'Aman Agnihotri pour en tenir compte en utilisant la map()
fonction pour 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;
}
}
la source
Invoice::total
vsinvoice -> invoice.total()
.Collectors.summingInt()
, mais les manque pourBigDecimal
s. Au lieu d'écrire cereduce(blah blah blah)
qui est difficile à lire, il serait préférable d'écrire le collecteur manquantBigDecimal
et de l'avoir.collect(summingBigDecimal())
à la fin de votre pipeline.Cette publication a déjà une réponse vérifiée, mais la réponse ne filtre pas les valeurs nulles. La bonne réponse doit empêcher les valeurs nulles en utilisant la fonction Object :: nonNull comme prédicat.
Cela empêche les valeurs nulles de tenter d'être additionnées lorsque nous réduisons.
la source
Vous pouvez résumer les valeurs d'un
BigDecimal
flux à l'aide d'un collecteur réutilisable nommé :summingUp
Le
Collector
peut être implémenté comme ceci:la source
Utilisez cette approche pour additionner la liste de BigDecimal:
Cette approche mappe chaque BigDecimal en tant que BigDecimal uniquement et les réduit en les additionnant, qui est ensuite renvoyée à l'aide de la
get()
méthode.Voici une autre façon simple de faire la même somme:
Mettre à jour
Si je devais écrire la classe et l'expression lambda dans la question éditée, je l'aurais écrite comme suit:
la source
.map(n -> n)
inutile là-bas? N'estget()
pas non plus nécessaire.get()
car il renvoie la valeur duOptional
qui est retourné par l'reduce
appel. Si l'on veut travailler avec leOptional
ou simplement imprimer la somme, alors oui, ceget()
n'est pas nécessaire. Mais l'impression de l'Optionnel imprime directement laOptional[<Value>]
syntaxe basée sur laquelle je doute que l'utilisateur aurait besoin. Ilget()
est donc nécessaire de manière à obtenir la valeur duOptional
.get
appel inconditionnel ! Sivalues
est une liste vide, l'option facultative ne contiendra aucune valeur et lancera unNoSuchElementException
quandget
est appelé. Vous pouvez utiliser à lavalues.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
place.Si une dépendance tierce ne vous dérange pas, il existe une classe nommée Collectors2 dans Eclipse Collections qui contient des méthodes renvoyant des Collectors pour additionner et résumer BigDecimal et BigInteger. Ces méthodes prennent une fonction comme paramètre afin que vous puissiez extraire une valeur BigDecimal ou BigInteger d'un objet.
Remarque: je suis un committer pour les collections Eclipse.
la source