BigDecimal - um new oder valueOf zu verwenden

97

Ich habe zwei Möglichkeiten gefunden, um ein BigDecimal-Objekt aus einem doppelten d herauszuholen.

1. new BigDecimal(d)
2. BigDecimal.valueOf(d)

Welches wäre ein besserer Ansatz? Würde valueOf ein neues Objekt erstellen?

Was wird im Allgemeinen (nicht nur BigDecimal) empfohlen - neu oder valueOf?

Vielen Dank.

Manish Mulani
quelle
9
Im Allgemeinen wird valueOf bevorzugt (da durch die Wiederverwendung "beliebter" Instanzen das Erstellen neuer Objekte vermieden werden kann). Bei BigDecimals und double führen die beiden Methoden jedoch leider zu unterschiedlichen Ergebnissen, sodass Sie auswählen müssen, welche Sie benötigen.
Thilo

Antworten:

160

Das sind zwei getrennte Fragen: "Wofür soll ich verwenden BigDecimal?" und "Was mache ich im Allgemeinen?"

Denn BigDecimal: das ist etwas knifflig, weil sie nicht dasselbe machen . BigDecimal.valueOf(double)verwendet die kanonische StringDarstellung des übergebenen doubleWerts, um das BigDecimalObjekt zu instanziieren . Mit anderen Worten: Der Wert des BigDecimalObjekts entspricht dem, was Sie dabei sehen System.out.println(d).

Wenn Sie new BigDecimal(d)jedoch verwenden, BigDecimalversucht der, den doubleWert so genau wie möglich darzustellen . Dies führt normalerweise dazu, dass viel mehr Ziffern gespeichert werden, als Sie möchten. Genau genommen ist es korrekter als valueOf(), aber es ist viel weniger intuitiv.

Im JavaDoc gibt es eine schöne Erklärung dafür:

Die Ergebnisse dieses Konstruktors können etwas unvorhersehbar sein. Man könnte annehmen, dass beim Schreiben new BigDecimal(0.1)in Java BigDecimalein Wert erstellt wird, der genau gleich 0,1 ist (ein nicht skalierter Wert von 1 mit einer Skala von 1), aber tatsächlich gleich 0,1000000000000000055511151231257827021181583404541015625. Dies liegt daran, dass 0,1 nicht genau als double(oder im Übrigen als binärer Bruchteil einer endlichen Länge) dargestellt werden kann. Somit ist der Wert, der an den Konstruktor übergeben wird, ungeachtet des Aussehens nicht genau gleich 0,1.

In der Regel , wenn das Ergebnis das gleiche ist (also nicht im Fall BigDecimal, aber in den meisten anderen Fällen), dann valueOf()sollte bevorzugt werden: es Caching von gemeinsamen Werten tun (wie gesehen auf Integer.valueOf()) und es kann sogar das Caching - Verhalten ändern , ohne Der Anrufer muss geändert werden. newwird immer einen neuen Wert, wenn auch nicht notwendig (: beste Beispiel instanziiert new Boolean(true)vs. Boolean.valueOf(true)).

Joachim Sauer
quelle
Dies erklärt auch meine Frage: stackoverflow.com/questions/15685705/…
Christian
3
@ Joachim, es war nicht klar. Ist new BigDecimal()besser als BigDecimal.valueOf()?
Ryvantage
5
@ryvantage: Wenn einer streng besser wäre als der andere, dann wären beide nicht nötig und meine Antwort wäre viel kürzer. Sie machen nicht das Gleiche, also kann man sie nicht so einordnen.
Joachim Sauer
2
@ JoachimSauer, ok sorry ich hätte genauer sein sollen. Würde es Ihnen etwas ausmachen, ein Beispiel dafür zu geben, wann new BigDecimal()bevorzugt wird und wann ein Beispiel BigDecimal.valueOf()bevorzugt wird?
Ryvantage
@ryvantage: Vergleichen Sie die Ergebnisse von new BigDecimal(1.0/30.0);und BigDecimal.valueOf(1.0/30.0). Sehen Sie, welches Ergebnis tatsächlich näher am numerischen Bruch 1/30 liegt.
Superkatze
46

Wenn Sie Ihre BigDecimalObjekte zum Speichern von Währungswerten verwenden, empfehle ich dringend, dass Sie KEINE doppelten Werte in ihre Berechnungen einbeziehen.

Wie in einer anderen Antwort angegeben, sind Genauigkeitsprobleme mit doppelten Werten bekannt, die Sie erneut verfolgen werden.

Sobald Sie darüber hinweg sind, ist die Antwort auf Ihre Frage einfach. Verwenden Sie immer die Konstruktormethode mit dem String-Wert als Argument für den Konstruktor, da es keine valueOfMethode für gibt String.

Wenn Sie einen Beweis wünschen, versuchen Sie Folgendes:

BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal("0.01");
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);

Sie erhalten folgende Ausgabe:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

Siehe auch diese verwandte Frage

DuncanKinnear
quelle
5

Grundsätzlich macht valueOf (double val) genau das:

return new BigDecimal(Double.toString(val));

Deshalb -> ja, wird ein neues Objekt erstellt :).

Im Allgemeinen denke ich, dass es von Ihrem Codierungsstil abhängt. Ich würde valueOf und "new" nicht mischen, wenn beide das gleiche Ergebnis sind.

DXTR66
quelle
7
Technisch wahr, aber : es wird einen großen Unterschied machen. valueOf()hat das intuitivere Verhalten, während new BigDecimal(d)das korrektere . Probieren Sie beide aus und sehen Sie den Unterschied.
Joachim Sauer
Technisch falsch. Das Schlüsselwort 'new' always erstellt immer ein neues Objekt, während das Javadoc nicht sagt, ob valueOf immer ein neues Objekt zurückgibt oder nicht. Das tut es nicht, nicht immer. Es hat einige Werte im Cache so new BigDecimal(1) != new BigDecimal(1)aberBigDecimal.valueOf(1) == BigDecimal.valueOf(1)
aalku
1
@user: Ja, aber da BigDecimalunveränderlich ist es sollte die gleiche Art und Weise behandelt werden , dass die primitiven Wrapper ( Integer, Byte, ...) und Stringbehandelt werden: Objektidentität sollte keine Rolle spielen, um Ihren Code, nur der Wert sollte Materie.
Joachim Sauer
@ Joachim Richtig, aber dieser interne Cache ist aus einem bestimmten Grund vorhanden. Zu viele nicht benötigte gleiche Instanzen von BigDecimal sind keine gute Sache. Und ich antwortete Dr., Er sagte, "ein neues Objekt wird erstellt"
Aalku
3
@user: ja, deshalb habe ich gesagt, das valueOf()sollte generell bevorzugt werden. Beachten Sie jedoch, dass BigDecimal.valueOf(double)kein Caching durchgeführt wird (und es sich wahrscheinlich auch nicht lohnt).
Joachim Sauer