BigDecimal - щоб використовувати new або valueOf


97

Я зіткнувся з двома способами виведення об’єкта BigDecimal із подвійного d.

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

Що було б кращим підходом? Чи буде valueOf створити новий об’єкт?

Загалом (не лише BigDecimal), що рекомендується - нове чи значенняOf?

Дякую.


9
Взагалі, valueOf є кращим (оскільки він може уникнути створення нових об’єктів шляхом повторного використання «популярних» екземплярів), але у випадку з BigDecimals та double, на жаль, обидва методи дають різні результати, тому вам потрібно вибрати, який саме вам потрібен.
Тіло

Відповіді:


160

Це два окремі запитання: "Для чого мені використовувати 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()слід віддати перевагу: він може виконувати кешування загальних значень (як показано далі Integer.valueOf()) і навіть може змінити поведінку кешування без абонента потрібно змінити. newбуде завжди інстанцірует нове значення, навіть якщо не потрібно (кращий приклад: new Boolean(true)проти Boolean.valueOf(true)).


Це також пояснює моє запитання: stackoverflow.com/questions/15685705/…
Крістіан

3
@Joachim, це було незрозуміло. Це new BigDecimal()краще ніж BigDecimal.valueOf()?
ryvantage

5
@ryvantage: якби одна з них була суворо кращою за іншу, тоді не було б потреби в обох, і моя відповідь була б набагато коротшою. Вони не роблять одне і те ж, тому їх не можна класифікувати так.
Йоахім Зауер

2
@JoachimSauer, добре, вибачте, я повинен був бути більш конкретним. Чи не могли б Ви навести приклад того, коли new BigDecimal()б віддали перевагу, і приклад, коли BigDecimal.valueOf()б віддали перевагу?
ryvantage

@ryvantage: Порівняйте результати new BigDecimal(1.0/30.0);та BigDecimal.valueOf(1.0/30.0). Подивіться, який результат насправді ближчий до числового дробу 1/30.
supercat

46

Якщо ви використовуєте свої 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", якщо обидва результати однакові.


7
Технічно вірно, але : це зробить величезну різницю. valueOf()має більш інтуїтивну поведінку, тоді як new BigDecimal(d)має більш правильну . Спробуйте обидва і переконайтеся в різниці.
Йоахім Зауер,

Технічно помилковий. Ключове слово 'new' завжди створює новий об'єкт, тоді як 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)кешування не виконується (і, ймовірно, це теж не варто було б).
Йоахім Зауер,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.