BigDecimal - использовать new или valueOf

101

Я нашел два способа получить объект BigDecimal из двойного d.

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

Что было бы лучше? Будет ли valueOf создавать новый объект?

В общем (не только BigDecimal) что рекомендуется - new или valueOf?

Спасибо.

Маниш Мулани
источник
10
В общем, valueOf является предпочтительным (поскольку он позволяет избежать создания новых объектов путем повторного использования «популярных» экземпляров), но в случае BigDecimals и double, к сожалению, эти два метода дают разные результаты, поэтому вам нужно выбрать, какой из них вам нужен.
Тило

Ответы:

163

Это два отдельных вопроса: «Для чего мне использовать BigDecimal?» и "Что мне вообще делать?"

For BigDecimal: это немного сложно, потому что они не делают то же самое . BigDecimal.valueOf(double)будет использовать каноническое Stringпредставление о doubleстоимости передаваемого в экземпляр BigDecimalобъекта. Другими словами: ценность BigDecimalобъекта будет тем, что вы видите, когда делаете System.out.println(d).

Однако, если вы используете new BigDecimal(d), то BigDecimalбудет пытаться представить doubleзначение как можно точнее . Это обычно приводит к хранению гораздо большего количества цифр, чем вы хотите. Строго говоря, это более правильно valueOf(), но намного менее интуитивно понятно.

В JavaDoc есть хорошее объяснение этого:

Результаты этого конструктора могут быть несколько непредсказуемыми. Можно предположить, что запись new BigDecimal(0.1)на Java создает a, BigDecimalкоторый точно равен 0,1 (немасштабированное значение 1, со шкалой 1), но на самом деле он равен 0,1000000000000000055511151231257827021181583404541015625. Это связано с тем, что 0,1 не может быть представлено точно как a double(или, если на то пошло, как двоичная дробь любой конечной длины). Таким образом, значение, передаваемое конструктору, не совсем равно 0,1, несмотря на внешний вид.

В общем, если результат такой же (то есть не в случае BigDecimal, а в большинстве других случаев), то valueOf()следует предпочесть: он может выполнять кэширование общих значений (как показано на Integer.valueOf()) и даже изменять поведение кеширования без необходимо изменить вызывающего абонента. всегдаnew будет создавать новое значение, даже если в этом нет необходимости (лучший пример: vs. ).new Boolean(true)Boolean.valueOf(true)

Иоахим Зауэр
источник
Это также объясняет мой вопрос: stackoverflow.com/questions/15685705/…
Christian
3
@Joachim, это было непонятно. Является ли new BigDecimal()лучше BigDecimal.valueOf()?
ryvantage
5
@ryvantage: если один будет строго лучше другого, тогда не будет необходимости в обоих, и мой ответ будет намного короче. Они не делают то же самое, поэтому вы не можете так ранжировать их.
Joachim Sauer
2
@JoachimSauer, извини, я должен был быть более конкретным. Не могли бы вы привести пример того, когда new BigDecimal()будет предпочтительнее, и пример того, когда BigDecimal.valueOf()предпочтительнее?
ryvantage
@ryvantage: Сравните результаты new BigDecimal(1.0/30.0);и BigDecimal.valueOf(1.0/30.0). Посмотрите, какой результат на самом деле ближе к числовой дроби 1/30.
supercat
48

Если вы используете свои BigDecimalобъекты для хранения валютных ценностей, я настоятельно рекомендую НЕ в своих расчетах какие-либо двойные значения.

Как указано в другом ответе, существуют известные проблемы с точностью с двойными значениями, и они снова будут вас преследовать.

Как только вы справитесь с этим, ответ на ваш вопрос будет прост. Всегда используйте метод конструктора со значением String в качестве аргумента конструктора, так как valueOfдля String.

Если вам нужны доказательства, попробуйте следующее:

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

Вы получите следующий результат:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

См. Также этот связанный вопрос

Дункан Киннер
источник
5

В основном valueOf (double val) просто делает это:

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

Поэтому -> да, будет создан новый объект :).

В общем, я думаю, это зависит от вашего стиля кодирования. Я бы не стал смешивать valueOf и new, если оба результата имеют одинаковый результат.

DXTR66
источник
7
Технически это правда, но это будет иметь огромное значение. valueOf()имеет более интуитивное поведение, а new BigDecimal(d)более правильное . Попробуйте оба варианта и увидите разницу.
Иоахим Зауэр
Технически неверно. Ключевое слово new always всегда создает новый объект, в то время как javadoc не сообщает, будет ли valueOf всегда возвращать новый объект или нет. Не всегда. У него есть некоторые значения в кеше, new BigDecimal(1) != new BigDecimal(1)ноBigDecimal.valueOf(1) == BigDecimal.valueOf(1)
aalku
1
@user: да, но так как BigDecimalнеизменен следует относиться так же, как примитивные обертки ( Integer, Byte, ...) и Stringтрактуются: идентификатор объекта должен не имеет значения в код, только значение должно иметь значение.
Иоахим Зауэр
@Joachim Верно, но этот внутренний кеш существует не просто так. Слишком много ненужных одинаковых экземпляров BigDecimal - не лучший вариант. И я отвечал доктору, он сказал: «Будет создан новый объект»
aalku
3
@user: да, поэтому я сказал, что это вообщеvalueOf() должно быть предпочтительнее. Но обратите внимание, что это не кеширует (и, вероятно, оно того не стоит). BigDecimal.valueOf(double)
Иоахим Зауэр