Hinzufügen von bis BigDecimals mit Streams
Habe ich eine Sammlung von BigDecimals (in diesem Beispiel ein LinkedList
), die ich hinzufügen möchte, zusammen. Ist es möglich streams für diese?
Bemerkte ich, dass die Stream
- Klasse hat mehrere Methoden
Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong
Jeder von denen hat eine bequeme sum()
Methode. Aber, wie wir wissen, ist float
und double
Arithmetik ist fast immer eine schlechte Idee.
So, ist es ein bequemer Weg, um die Summe bis BigDecimals?
Dies ist der code, den ich bisher haben.
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());
}
Wie Sie sehen können, ich bin fasst die BigDecimals mit BigDecimal::doubleValue()
, aber das ist (wie erwartet) nicht präzise ist.
Post-beantworten, Bearbeiten, für die Nachwelt:
Beide Antworten waren sehr hilfreich. Ich wollte noch hinzufügen, ein wenig: mein real-life-Szenario nicht um eine Sammlung von raw - BigDecimal
s, Sie sind eingehüllt in eine Rechnung. Aber, ich war in der Lage zu ändern Aman Agnihotri Antwort, um für dieses Konto durch die Nutzung der map()
- Funktion für den 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;
}
}
InformationsquelleAutor ryvantage | 2014-03-25
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ursprüngliche Antwort
Ja, das ist möglich:
Was es tut ist:
List<BigDecimal>
.Stream<BigDecimal>
Aufruf der Methode reduzieren.
3.1. Wir liefern ein identity-Wert für die addition, nämlich
BigDecimal.ZERO
.3.2. Wir geben die
BinaryOperator<BigDecimal>
fügt zweiBigDecimal
's, über eine Methode ReferenzBigDecimal::add
.Aktualisierte Antwort, nach Bearbeiten
Ich sehe, Sie haben neue Daten, daher neue Antwort zu:
Ist es meistens dasselbe, außer dass ich Hinzugefügt eine
totalMapper
variable, die eine Funktion vonInvoice
zuBigDecimal
und gibt den Gesamtpreis der Rechnung.Dann besorge ich eine
Stream<Invoice>
, ordnen Sie Sie mit einemStream<BigDecimal>
und dann reduzieren Sie es auf einBigDecimal
.Nun, von einem OOP-design Punkt würde ich empfehlen, dass Sie auch tatsächlich die
total()
Methode, die Sie bereits definiert haben, dann ist es noch einfacher wird:Hier haben wir direkt die Methode Referenz in der
map
Methode.+1 für
Invoice::total
vsinvoice -> invoice.total()
.+1 für das Verfahren und die Referenzen für das hinzufügen von Zeilenumbrüchen zwischen stream-Operationen, die beide IMHO die Lesbarkeit erheblich.
Die saubere Lösung. +1
Dieser Ansatz kann dazu führen, NullponterException
InformationsquelleAutor skiwi
Diesen Ansatz verwenden, um die Summe der Liste von BigDecimal:
Dieser Ansatz ordnet jedem BigDecimal als BigDecimal nur auf und reduzieren Sie durch Sie zu addieren, die dann zurückgegeben mit der
get()
Methode.Hier ist eine weitere einfache Möglichkeit, das gleiche zu tun Fazit:
Update
Wenn ich die zum schreiben der Klasse und lambda-Ausdruck in die bearbeitete Frage, ich würde geschrieben haben es wie folgt:
.map(n -> n)
nutzlos? Auchget()
ist nicht erforderlich.Aktualisiert. Danke. Ich verwendet
get()
da gibt es den Wert derOptional
, der zurückgegeben wird, durch diereduce
nennen. Wenn man arbeiten will, mit derOptional
oder einfach nur drucken Sie die Summe, dann, ja,get()
ist nicht erforderlich. Aber drucken die Optionale direkt-DruckeOptional[<Value>]
- basierte syntax, die ich bezweifle, dass der Benutzer benötigen würde. Soget()
ist notwendig, einen Weg, um den Wert aus derOptional
.check meine OP für meinen edit.
Ja, dein Ansatz ist genau so wie ich es getan hätte. 🙂
Verwenden Sie nicht einen bedingungslosen
get
Anruf! Wennvalues
ist eine leere Liste, die optional enthalten keinen Wert und wirft einenNoSuchElementException
wennget
genannt wird. Sie könnenvalues.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
statt.InformationsquelleAutor Aman Agnihotri
Dieser Beitrag hat bereits eine geprüfte Antwort, aber die Antwort nicht filter für null-Werte. Die richtige Antwort sollte verhindern, dass null-Werte mithilfe der Objekt -:: - null Funktion als Prädikat.
Dies verhindert, dass null-Werte aus dem Versuch, die summiert werden, wie wir Sie reduzieren.
InformationsquelleAutor Siraj
Können Sie die Summe der Werte einer
BigDecimal
- stream über einen wiederverwendbare Sammler namenssummingUp
:Den
Collector
umgesetzt werden können, wie diese:InformationsquelleAutor Igor Akkerman
Wenn dir nichts ausmacht eine Dritte Partei Abhängigkeit, es gibt eine Klasse namens Collectors2 in Eclipse-Sammlungen enthält Methoden, die Rückkehr Sammler für Fazit und Zusammenfassung BigDecimal und BigInteger. Diese Methoden nehmen eine Funktion als parameter, so können Sie extrahieren eine BigDecimal-oder BigInteger-Wert aus einem Objekt.
Hinweis: ich bin ein committer für Eclipse Sammlungen.
InformationsquelleAutor Donald Raab