BigDecimal - para usar new ou valueOf

97

Me deparei com duas maneiras de obter um objeto BigDecimal de um d duplo.

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

Qual seria a melhor abordagem? ValueOf criaria um novo objeto?

Em geral (não apenas BigDecimal), o que é recomendado - new ou valueOf?

Obrigado.

Manish Mulani
fonte
9
Em geral, valueOf é o preferido (porque pode evitar a criação de novos objetos reutilizando instâncias "populares"), mas no caso de BigDecimals e double, infelizmente, os dois métodos produzem resultados diferentes, então você deve escolher qual deles você precisa.
Thilo

Respostas:

160

Essas são duas perguntas distintas: "O que devo usar BigDecimal?" e "O que eu faço em geral?"

Pois BigDecimal: isso é um pouco complicado, porque eles não fazem a mesma coisa . BigDecimal.valueOf(double)usará a representação canônicaString do doublevalor passado para instanciar o BigDecimalobjeto. Em outras palavras: o valor do BigDecimalobjeto será o que você vê ao fazer System.out.println(d).

Se você usar new BigDecimal(d), no entanto, o BigDecimaltentará representar o doublevalor com a maior precisão possível . Isso geralmente resultará em muito mais dígitos armazenados do que você deseja. A rigor, é mais correto do que valueOf(), mas é muito menos intuitivo.

Há uma boa explicação disso no JavaDoc:

Os resultados desse construtor podem ser um tanto imprevisíveis. Pode-se supor que a gravação new BigDecimal(0.1)em Java cria um BigDecimalque é exatamente igual a 0,1 (um valor fora de escala de 1, com uma escala de 1), mas na verdade é igual a 0,1000000000000000055511151231257827021181583404541015625. Isso ocorre porque 0,1 não pode ser representado exatamente como um double(ou, nesse caso, como uma fração binária de qualquer comprimento finito). Portanto, o valor que está sendo passado para o construtor não é exatamente igual a 0,1, apesar das aparências.

Em geral, se o resultado for o mesmo (ou seja, não no caso de BigDecimal, mas na maioria dos outros casos), então valueOf()deve ser preferido: ele pode fazer o cache de valores comuns (como visto em Integer.valueOf()) e pode até mesmo alterar o comportamento do cache sem o chamador deve ser alterado. newvai sempre instanciar um novo valor, mesmo que não seja necessário (melhor exemplo: new Boolean(true)vs. Boolean.valueOf(true)).

Joachim Sauer
fonte
Isso também explica minha pergunta: stackoverflow.com/questions/15685705/…
Christian
3
@Joachim, não ficou claro. É new BigDecimal()melhor do que BigDecimal.valueOf()?
ryvantage
5
@ryvantage: se um fosse estritamente melhor do que o outro, não haveria necessidade de ambos e minha resposta seria muito mais curta. Eles não fazem a mesma coisa, então você não pode classificá-los dessa forma.
Joachim Sauer
2
@JoachimSauer, ok desculpe, eu deveria ter sido mais específico. Você se importaria de dar um exemplo de quando new BigDecimal()seria preferido e um exemplo de quando BigDecimal.valueOf()seria preferido?
ryvantage
@ryvantage: compare os resultados de new BigDecimal(1.0/30.0);e BigDecimal.valueOf(1.0/30.0). Veja qual resultado está realmente mais próximo da fração numérica 1/30.
supercat
46

Se você estiver usando seus BigDecimalobjetos para armazenar valores monetários, então eu recomendo fortemente que você NÃO envolva nenhum valor duplo em seus cálculos.

Conforme afirmado em outra resposta, existem problemas de precisão conhecidos com valores duplos e eles voltarão para assombrá-lo.

Depois de superar isso, a resposta à sua pergunta é simples. Sempre use o método do construtor com o valor String como o argumento do construtor, pois não há valueOfmétodo para String.

Se você quiser uma prova, tente o seguinte:

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

Você obterá a seguinte saída:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

Veja também esta questão relacionada

DuncanKinnear
fonte
5

Basicamente, valueOf (double val) faz apenas isso:

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

Portanto -> sim, um novo objeto será criado :).

Em geral, acho que depende do seu estilo de codificação. Eu não misturaria valueOf e "new", se ambos fossem o mesmo resultado.

DXTR66
fonte
7
Tecnicamente verdade, mas : fará uma grande diferença. valueOf()tem o comportamento mais intuitivo , enquanto o new BigDecimal(d)tem o mais correto . Experimente os dois e veja a diferença.
Joachim Sauer
Tecnicamente falso. A palavra-chave 'new' always sempre cria um novo objeto, enquanto o javadoc não informa se valueOf sempre retornará um novo objeto ou não. Isso não acontece, nem sempre. Ele tem alguns valores no cache, new BigDecimal(1) != new BigDecimal(1)masBigDecimal.valueOf(1) == BigDecimal.valueOf(1)
aalku
1
@user: sim, mas desde que BigDecimalé imutável deve ser tratada da mesma forma que os wrappers primitivos ( Integer, Byte...) e Stringsão tratados: a identidade do objeto deve não importa ao seu código, apenas o valor deve importar.
Joachim Sauer
@Joachim Certo, mas esse cache interno existe por um motivo. Muitas instâncias iguais não necessárias de BigDecimal não são boas. E eu estava respondendo ao Dr., Ele disse "um novo objeto será criado"
aalku
3
@ usuário: sim, é por isso que eu disse que geralmentevalueOf() deveria ser preferido. Mas observe que isso não faz nenhum cache (e provavelmente não valeria a pena também). BigDecimal.valueOf(double)
Joachim Sauer