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

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

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

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

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

Спасибо.


person Manish Mulani    schedule 25.08.2011    source источник
comment
В общем, valueOf предпочтительнее (поскольку он позволяет избежать создания новых объектов за счет повторного использования популярных экземпляров), но в случае с BigDecimals и double, к сожалению, два метода дают разные результаты, поэтому вам придется выбирать, какой из них вам нужен.   -  person Thilo    schedule 25.08.2011


Ответы (3)


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

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

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

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

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

В общем, если результат один и тот же (т.е. не в случае BigDecimal, а в большинстве других случаев), то предпочтение следует отдавать valueOf(): он умеет кэшировать общие значения (как видно на < a href="http://download.oracle.com/javase/7/docs/api/java/lang/Integer.html#valueOf%28int%29" rel="noreferrer">Integer.valueOf()) и может даже изменить поведение кэширования без изменения вызывающего объекта. new будет всегда создавать новое значение, даже если в этом нет необходимости (лучший пример: new Boolean(true) против Boolean.valueOf(true)).

person Joachim Sauer    schedule 25.08.2011
comment
Это также объясняет мой вопрос: stackoverflow.com/questions/15685705/ - person Christian; 28.03.2013
comment
@ Иоахим, это было неясно. new BigDecimal() лучше, чем BigDecimal.valueOf()? - person ryvantage; 25.12.2013
comment
@ryvantage: если бы один был строго лучше другого, тогда не было бы необходимости в обоих, и мой ответ был бы намного короче. Они не делают одно и то же, поэтому вы не можете ранжировать их таким образом. - person Joachim Sauer; 25.12.2013
comment
@JoachimSauer, хорошо, извини, я должен был быть более конкретным. Не могли бы вы привести пример, когда new BigDecimal() будет предпочтительнее, а когда BigDecimal.valueOf() будет предпочтительнее? - person ryvantage; 25.12.2013
comment
@ryvantage: Сравните результаты new BigDecimal(1.0/30.0); и BigDecimal.valueOf(1.0/30.0). Посмотрите, какой результат на самом деле ближе к числовой дроби 1/30. - person supercat; 05.04.2014
comment
valueOf() следует предпочесть: он может кэшировать общие значения Я только что посмотрел исходный код для BigDecimal.valueOf(double), и он НЕ выполняет никакого кэширования. У него всего одна строка: return new BigDecimal(Double.toString(val));, и она всегда должна создавать новый объект. (Я тоже поверил лжи, так что сам в тупике.) - person ADTC; 01.02.2018
comment
Это укусило меня несколько раз. Когда у меня есть двойник, который отображается как .3065, я ожидаю, что мой BigDecimal также будет .3065... Не так при использовании new! Теперь я просто по умолчанию использую valueOf (если мне специально не требуется другое), поскольку подавляющее большинство моей работы зависит от точного совпадения точности (текущие отраслевые требования). - person Brian Knoblauch; 12.04.2018
comment
@BrianKnoblauch: по иронии судьбы, новый на самом деле более точно соответствует входному двойному значению, и именно вывод toString обманывает, отрезая правильные цифры, но не необходимые, чтобы отличить одно двойное значение от другого. - person Joachim Sauer; 12.04.2018
comment
@JoachimSauer Да, что я действительно хочу сделать, так это удалить все двойники и полностью преобразовать в BigDecimal, но это было бы серьезной задачей для приложения, чья дата EOL приближается. :-) - person Brian Knoblauch; 12.04.2018
comment
но новый BigDecimal(0.99) стал 0.989999..., что возвращает мне неправильный результат - person Beeing Jk; 28.01.2019
comment
@BeeingJk: да, и в этом ответе я подробно объясняю, почему это так и как это исправить. - person Joachim Sauer; 30.01.2019

Если вы используете объекты 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

См. также это >похожий вопрос

person DuncanKinnear    schedule 15.03.2012

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

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

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

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

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