BigDecimal - pour utiliser new ou valueOf

97

Je suis tombé sur deux façons de sortir un objet BigDecimal d'un double d.

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

Quelle serait la meilleure approche? ValueOf créerait-il un nouvel objet?

En général (pas seulement BigDecimal), qu'est-ce qui est recommandé - nouveau ou valueOf?

Merci.

Manish Mulani
la source
9
En général, valueOf est préférable (car il peut éviter de créer de nouveaux objets en réutilisant des instances "populaires"), mais dans le cas de BigDecimals et double, malheureusement, les deux méthodes produisent des résultats différents, vous devez donc choisir celle dont vous avez besoin.
Thilo

Réponses:

160

Ce sont deux questions distinctes: "Pour quoi dois-je utiliser BigDecimal?" et "Que dois-je faire en général?"

Pour BigDecimal: c'est un peu délicat, car ils ne font pas la même chose . BigDecimal.valueOf(double)utilisera la représentation canoniqueString de la doublevaleur transmise pour instancier l' BigDecimalobjet. En d'autres termes: la valeur de l' BigDecimalobjet sera ce que vous voyez lorsque vous le faites System.out.println(d).

Si vous utilisez new BigDecimal(d)cependant, alors BigDecimalessaiera de représenter la doublevaleur aussi précisément que possible . Cela entraînera généralement le stockage de beaucoup plus de chiffres que vous ne le souhaitez. À proprement parler, c'est plus correct que valueOf(), mais c'est beaucoup moins intuitif.

Il y a une belle explication à cela dans le JavaDoc:

Les résultats de ce constructeur peuvent être quelque peu imprévisibles. On pourrait supposer que l'écriture new BigDecimal(0.1)en Java crée un BigDecimalqui est exactement égal à 0,1 (une valeur non mise à l'échelle de 1, avec une échelle de 1), mais il est en fait égal à 0.1000000000000000055511151231257827021181583404541015625. C'est parce que 0,1 ne peut pas être représenté exactement comme a double(ou, d'ailleurs, comme une fraction binaire de toute longueur finie). Ainsi, la valeur qui est transmise au constructeur n'est pas exactement égale à 0,1, malgré les apparences.

En général, si le résultat est le même (c'est-à-dire pas dans le cas de BigDecimal, mais dans la plupart des autres cas), alors valueOf()devrait être préféré: il peut faire la mise en cache des valeurs communes (comme on le voit sur Integer.valueOf()) et il peut même changer le comportement de mise en cache sans l'appelant doit être changé. newsera toujours instancier une nouvelle valeur, même si pas nécessaire (meilleur exemple: par new Boolean(true)rapport Boolean.valueOf(true)).

Joachim Sauer
la source
Cela explique aussi ma question: stackoverflow.com/questions/15685705/…
Christian
3
@Joachim, ce n'était pas clair. Est-ce new BigDecimal()mieux que BigDecimal.valueOf()?
ryvantage
5
@ryvantage: si l'un était strictement meilleur que l'autre, il n'y aurait pas besoin des deux et ma réponse serait beaucoup plus courte. Ils ne font pas la même chose, donc vous ne pouvez pas les classer comme ça.
Joachim Sauer
2
@JoachimSauer, ok désolé j'aurais dû être plus précis. Pourriez-vous donner un exemple de quand new BigDecimal()serait préféré et un exemple de quand BigDecimal.valueOf()serait préféré?
ryvantage
@ryvantage: Comparez les résultats de new BigDecimal(1.0/30.0);et BigDecimal.valueOf(1.0/30.0). Voir quel résultat est en fait plus proche de la fraction numérique 1/30.
supercat
46

Si vous utilisez vos BigDecimalobjets pour stocker des valeurs monétaires, je vous recommande fortement de ne PAS impliquer de valeurs doubles dans leurs calculs.

Comme indiqué dans une autre réponse, il existe des problèmes de précision connus avec des valeurs doubles et ceux-ci reviendront vous hanter beaucoup.

Une fois que vous avez passé cela, la réponse à votre question est simple. Utilisez toujours la méthode constructeur avec la valeur String comme argument du constructeur, car il n'y a pas de valueOfméthode pour String.

Si vous voulez une preuve, essayez ce qui suit:

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

Vous obtiendrez la sortie suivante:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

Voir aussi cette question connexe

DuncanKinnear
la source
5

Fondamentalement, valueOf (double val) fait simplement ceci:

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

Par conséquent -> oui, un nouvel objet sera créé :).

En général, je pense que cela dépend de votre style de codage. Je ne mélangerais pas valueOf et "new", si les deux sont le même résultat.

DXTR66
la source
7
Techniquement vrai, mais : cela fera une énorme différence. valueOf()a le comportement le plus intuitif , tandis qu'il new BigDecimal(d)a le plus correct . Essayez les deux et voyez la différence.
Joachim Sauer
Techniquement faux. Le mot-clé 'new' always crée toujours un nouvel objet tandis que le javadoc ne dit pas si valueOf retournera toujours un nouvel objet ou non. Ce n'est pas le cas, pas toujours. Il a quelques valeurs dans le cache donc new BigDecimal(1) != new BigDecimal(1)maisBigDecimal.valueOf(1) == BigDecimal.valueOf(1)
aalku
1
@user: oui, mais comme elle BigDecimalest immuable , il doit être traité de la même manière que les emballages primitifs ( Integer, Byte, ...) et Stringsont traités: l' identité de l' objet ne doit pas question à votre code, seule la valeur devrait être importante.
Joachim Sauer
@Joachim Oui, mais ce cache interne est là pour une raison. Trop d'instances égales non nécessaires de BigDecimal ne sont pas une bonne chose à avoir. Et je répondais au Dr, Il a dit "un nouvel objet sera créé"
aalku
3
@user: oui, c'est pourquoi j'ai dit que cela valueOf()devrait généralement être préféré. Mais notez que BigDecimal.valueOf(double)cela ne fait aucune mise en cache (et cela n'en vaudrait probablement pas la peine non plus).
Joachim Sauer