BigDecimal - aby użyć new lub valueOf

101

Natknąłem się na dwa sposoby uzyskania obiektu BigDecimal z podwójnego d.

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

Które podejście byłoby lepsze? Czy valueOf stworzyłoby nowy obiekt?

Ogólnie (nie tylko BigDecimal), co jest zalecane - new czy valueOf?

Dzięki.

Manish Mulani
źródło
10
Ogólnie rzecz biorąc, valueOf jest preferowana (ponieważ pozwala uniknąć tworzenia nowych obiektów poprzez ponowne użycie „popularnych” instancji), ale w przypadku BigDecimals i double, niestety, dwie metody dają różne wyniki, więc musisz wybrać, której potrzebujesz.
Thilo,

Odpowiedzi:

165

To są dwa osobne pytania: „Do czego mam używać BigDecimal?” i „Co ogólnie robię?”

Bo BigDecimal: jest to trochę trudne, ponieważ nie robią tego samego . BigDecimal.valueOf(double)użyje kanoniczną Stringreprezentację na doublewartość przekazana do instancji BigDecimalobiektu. Innymi słowy: wartością BigDecimalobiektu będzie to, co zobaczysz, kiedy to zrobisz System.out.println(d).

Jeśli new BigDecimal(d)jednak użyjesz , BigDecimalspróbuje przedstawić doublewartość tak dokładnie, jak to możliwe . Będzie to zazwyczaj powodują dużo więcej cyfr są przechowywane niż chcesz. Ściśle mówiąc, jest to bardziej poprawne niż valueOf(), ale jest o wiele mniej intuicyjne.

Jest ładne wyjaśnienie tego w JavaDoc:

Wyniki tego konstruktora mogą być nieco nieprzewidywalne. Można założyć, że pisanie new BigDecimal(0.1)w Javie tworzy wartość BigDecimalrówną dokładnie 0,1 (nieskalowana wartość 1 ze skalą 1), ale w rzeczywistości jest równa 0,1000000000000000055511151231257827021181583404541015625. Dzieje się tak, ponieważ 0.1 nie może być reprezentowane dokładnie jako a double(lub, jeśli o to chodzi, jako ułamek binarny o dowolnej skończonej długości). W związku z tym wartość, która jest przekazywana do konstruktora, nie jest dokładnie równa 0,1, niezależnie od wyglądu.

Ogólnie, jeśli wynik jest taki sam (tj. Nie w przypadku BigDecimal, ale w większości innych przypadków), to valueOf()powinno być preferowane: może wykonywać buforowanie wspólnych wartości (jak widać na Integer.valueOf()), a nawet może zmieniać zachowanie buforowania bez osoba dzwoniąca musi zostać zmieniona. zawszenew utworzy instancję nowej wartości, nawet jeśli nie jest to konieczne (najlepszy przykład: vs. ).new Boolean(true)Boolean.valueOf(true)

Joachim Sauer
źródło
To również wyjaśnia moje pytanie: stackoverflow.com/questions/15685705/ ...
Christian
3
@Joachim, to nie było jasne. Jest new BigDecimal()lepsze niż BigDecimal.valueOf()?
ryvantage
5
@ryvantage: jeśli jeden byłby ściśle lepszy od drugiego, nie byłoby potrzeby obu, a moja odpowiedź byłaby znacznie krótsza. Nie robią tego samego, więc nie możesz ich tak oceniać.
Joachim Sauer
2
@JoachimSauer, ok przepraszam, powinienem był być bardziej szczegółowy. Czy mógłbyś podać przykład, kiedy new BigDecimal()byłby preferowany, a kiedy BigDecimal.valueOf()byłby preferowany?
ryvantage
@ryvantage: porównaj wyniki new BigDecimal(1.0/30.0);i BigDecimal.valueOf(1.0/30.0). Zobacz, który wynik jest rzeczywiście bliższy ułamkowi liczbowemu 1/30.
supercat
48

Jeśli używasz swoich BigDecimalobiektów do przechowywania wartości walut, zdecydowanie zalecam, aby w swoich obliczeniach NIE wprowadzać żadnych podwójnych wartości.

Jak stwierdzono w innej odpowiedzi, istnieją znane problemy z dokładnością związane z podwójnymi wartościami, które będą Cię prześladować.

Gdy już to minie, odpowiedź na twoje pytanie jest prosta. Zawsze używaj metody konstruktora z wartością String jako argumentem konstruktora, ponieważ nie ma valueOfmetody dla String.

Jeśli chcesz uzyskać dowód, wypróbuj następujące rozwiązania:

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

Otrzymasz następujące dane wyjściowe:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

Zobacz także to powiązane pytanie

DuncanKinnear
źródło
5

Zasadniczo valueOf (double val) robi to:

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

Dlatego -> tak, zostanie utworzony nowy obiekt :).

Ogólnie myślę, że zależy to od Twojego stylu kodowania. Nie mieszałbym valueOf i „nowego”, jeśli oba są tym samym wynikiem.

DXTR66
źródło
7
Technicznie prawda, ale : to będzie ogromna różnica. valueOf()ma bardziej intuicyjne zachowanie, podczas gdy new BigDecimal(d)ma bardziej poprawne . Wypróbuj oba i zobacz różnicę.
Joachim Sauer,
Technicznie fałszywe. Słowo kluczowe „new” always zawsze tworzy nowy obiekt, podczas gdy javadoc nie mówi, czy wartość valueOf zwróci zawsze nowy obiekt, czy nie. Nie zawsze. Ma pewne wartości w pamięci podręcznej, new BigDecimal(1) != new BigDecimal(1)aleBigDecimal.valueOf(1) == BigDecimal.valueOf(1)
aalku
1
@user: tak, ale ponieważ BigDecimaljest niezmienna to powinny być traktowane w ten sam sposób, że prymitywne owijarki ( Integer, Byte...) i Stringsą traktowane: tożsamość obiekt nie powinien sprawa do kodu, tylko wartość powinno mieć znaczenia.
Joachim Sauer,
@Joachim Racja, ale ta wewnętrzna pamięć podręczna istnieje nie bez powodu. Zbyt wiele niepotrzebnych równych wystąpień BigDecimal nie jest dobrym rozwiązaniem. A ja odpowiadałem dr. Powiedział: „zostanie utworzony nowy obiekt”
aalku
3
@user: tak, dlatego, że valueOf()powinny generalnie być korzystne. Ale pamiętaj, że BigDecimal.valueOf(double)nie wykonuje żadnego buforowania (i prawdopodobnie też nie byłoby tego warte).
Joachim Sauer,