Ich habe eine Sammlung von BigDecimals (in diesem Beispiel a LinkedList
), die ich zusammenfügen möchte. Ist es möglich, dafür Streams zu verwenden?
Mir ist aufgefallen, dass die Stream
Klasse mehrere Methoden hat
Stream::mapToInt
Stream::mapToDouble
Stream::mapToLong
Jedes davon hat eine bequeme sum()
Methode. Aber, wie wir wissen, float
und double
Arithmetik ist fast immer eine schlechte Idee.
Gibt es eine bequeme Möglichkeit, BigDecimals zusammenzufassen?
Dies ist der Code, den ich bisher habe.
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, fasse ich die BigDecimals mit zusammen BigDecimal::doubleValue()
, aber dies ist (wie erwartet) nicht genau.
Post-Answer-Bearbeitung für die Nachwelt:
Beide Antworten waren äußerst hilfreich. Ich wollte ein wenig hinzufügen: Mein reales Szenario beinhaltet keine Sammlung von Rohdaten BigDecimal
, sie sind in einer Rechnung verpackt. Aber ich konnte Aman Agnihotris Antwort ändern, um dies zu berücksichtigen, indem ich die map()
Funktion für Stream verwendete:
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;
}
}
quelle
Invoice::total
vsinvoice -> invoice.total()
.Collectors.summingInt()
, die jedoch fürBigDecimal
s fehlen . Anstattreduce(blah blah blah)
schwer lesbares Schreiben zu schreiben , ist es besser, einen fehlenden Kollektor fürBigDecimal
und.collect(summingBigDecimal())
am Ende Ihrer Pipeline zu schreiben .Dieser Beitrag hat bereits eine überprüfte Antwort, aber die Antwort filtert nicht nach Nullwerten. Die richtige Antwort sollte Nullwerte verhindern, indem die Object :: nonNull-Funktion als Prädikat verwendet wird.
Dies verhindert, dass Nullwerte beim Reduzieren summiert werden.
quelle
Sie können die Werte eines
BigDecimal
Streams mit einem wiederverwendbaren Collector mit folgenden Namen zusammenfassensummingUp
:Das
Collector
kann folgendermaßen implementiert werden:quelle
Verwenden Sie diesen Ansatz, um die Liste von BigDecimal zusammenzufassen:
Dieser Ansatz ordnet jedes BigDecimal nur einem BigDecimal zu und reduziert es durch Summieren, das dann mithilfe der
get()
Methode zurückgegeben wird.Hier ist eine weitere einfache Möglichkeit, dieselbe Summierung durchzuführen:
Aktualisieren
Wenn ich den Klassen- und Lambda-Ausdruck in die bearbeitete Frage schreiben würde, hätte ich ihn wie folgt geschrieben:
quelle
.map(n -> n)
nutzlos? Wird auchget()
nicht benötigt.get()
da es den Wert von zurückgibt, derOptional
vomreduce
Aufruf zurückgegeben wird. Wenn man mit dem arbeitenOptional
oder einfach die Summe ausdrucken will, dann ist das jaget()
nicht nötig. Beim Drucken der Optionalen Option wird jedoch direktOptional[<Value>]
die Syntax gedruckt, die der Benutzer meines Erachtens nicht benötigen würde. Wirdget()
also in gewisser Weise benötigt, um den Wert aus dem zu erhaltenOptional
.get
Anruf! Wennvalues
es sich um eine leere Liste handelt, enthält das optionale Element keinen Wert und löst einNoSuchElementException
Wann aus,get
das aufgerufen wird. Sie könnenvalues.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO)
stattdessen verwenden.Wenn Ihnen eine Abhängigkeit von Drittanbietern nichts ausmacht, gibt es in Eclipse Collections eine Klasse namens Collectors2, die Methoden enthält, die Collectors zum Summieren und Zusammenfassen von BigDecimal und BigInteger zurückgeben. Diese Methoden verwenden eine Funktion als Parameter, sodass Sie einen BigDecimal- oder BigInteger-Wert aus einem Objekt extrahieren können.
Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.
quelle