BigDecimal - để sử dụng new hoặc valueOf


97

Tôi đã xem qua hai cách để lấy đối tượng BigDecimal ra khỏi một đôi d.

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

Cách tiếp cận nào sẽ tốt hơn? ValueOf có tạo một đối tượng mới không?

Nói chung (không chỉ BigDecimal), điều gì được khuyến nghị - mới hay giá trị?

Cảm ơn.


9
Nói chung, valueOf được ưa thích hơn (vì nó có thể tránh tạo các đối tượng mới bằng cách sử dụng lại các trường hợp "phổ biến"), nhưng trong trường hợp BigDecimals và double, thật không may, hai phương pháp cho kết quả khác nhau, vì vậy bạn phải chọn cái nào bạn cần.
Thilo

Câu trả lời:


160

Đó là hai câu hỏi riêng biệt: "Tôi nên sử dụng để làm BigDecimalgì?" và "Nói chung tôi phải làm gì?"

BigDecimal: điều này hơi khó, vì họ không làm điều tương tự . BigDecimal.valueOf(double)sẽ sử dụng biểu diễn chính tắcString của doublegiá trị được truyền vào để khởi tạo BigDecimalđối tượng. Nói cách khác: Giá trị của BigDecimalđối tượng sẽ là những gì bạn nhìn thấy khi làm System.out.println(d).

new BigDecimal(d)Tuy nhiên, nếu bạn sử dụng , thì dấu BigDecimalsẽ cố gắng thể hiện doublegiá trị chính xác nhất có thể . Điều này thường sẽ dẫn đến việc lưu trữ nhiều chữ số hơn bạn muốn. Nói một cách chính xác, nó đúng hơn valueOf(), nhưng nó kém trực quan hơn rất nhiều.

Có một lời giải thích hay về điều này trong JavaDoc:

Kết quả của hàm tạo này có thể hơi khó đoán. Người ta có thể giả định rằng viết bằng new BigDecimal(0.1)Java tạo ra một BigDecimalgiá trị chính xác bằng 0,1 (một giá trị chưa được chia tỷ lệ là 1, với tỷ lệ là 1), nhưng nó thực sự bằng 0,10000000000000055511151231257827021181583404541015625. Điều này là do 0,1 không thể được biểu diễn chính xác dưới dạng a double(hoặc, đối với vấn đề đó, dưới dạng phân số nhị phân của bất kỳ độ dài hữu hạn nào). Do đó, giá trị đang được chuyển vào hàm tạo không chính xác bằng 0,1, mặc dù xuất hiện.

Nói chung, nếu kết quả giống nhau (nghĩa là không phải trong trường hợp của BigDecimal, mà trong hầu hết các trường hợp khác), thì valueOf()nên được ưu tiên hơn: nó có thể thực hiện lưu vào bộ nhớ đệm của các giá trị chung (như đã thấy ở trên Integer.valueOf()) và thậm chí nó có thể thay đổi hành vi bộ nhớ đệm mà không người gọi phải được thay đổi. newsẽ luôn khởi tạo một giá trị mới, ngay cả khi không cần thiết (ví dụ tốt nhất: new Boolean(true)so với Boolean.valueOf(true)).


Điều này cũng giải thích câu hỏi của tôi: stackoverflow.com/questions/15685705/…
Christian

3
@Joachim, nó không rõ ràng. Là new BigDecimal()tốt hơn BigDecimal.valueOf()?
ryvantage

5
@ryvantage: nếu cái nào tốt hơn cái kia thì sẽ không cần cả hai và câu trả lời của tôi sẽ ngắn hơn nhiều. Họ không làm điều tương tự, vì vậy bạn không thể xếp hạng họ ở mức như vậy.
Joachim Sauer

2
@JoachimSauer, được rồi, xin lỗi tôi nên nói cụ thể hơn. Bạn có vui lòng đưa ra một ví dụ về thời điểm new BigDecimal()được ưu tiên và ví dụ về thời điểm BigDecimal.valueOf()được ưu tiên không?
ryvantage

@ryvantage: So sánh kết quả của new BigDecimal(1.0/30.0);BigDecimal.valueOf(1.0/30.0). Xem kết quả nào thực sự gần với phân số 1/30 hơn.
supercat

46

Nếu bạn đang sử dụng các BigDecimalđối tượng của mình để lưu trữ các giá trị tiền tệ, thì tôi thực sự khuyên bạn KHÔNG nên liên quan đến bất kỳ giá trị kép nào ở bất kỳ đâu trong tính toán của chúng.

Như đã nêu trong một câu trả lời khác, có những vấn đề về độ chính xác đã biết với các giá trị kép và những vấn đề này sẽ trở lại ám ảnh bạn trong thời gian dài.

Khi bạn vượt qua điều đó, câu trả lời cho câu hỏi của bạn rất đơn giản. Luôn sử dụng phương thức hàm tạo với giá trị Chuỗi làm đối số cho hàm tạo, vì không có valueOfphương thức nào cho String.

Nếu bạn muốn có bằng chứng, hãy thử cách sau:

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

Bạn sẽ nhận được kết quả sau:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

Xem thêm câu hỏi liên quan này


5

Về cơ bản valueOf (double val) chỉ thực hiện điều này:

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

Do đó -> yep, một đối tượng mới sẽ được tạo :).

Nói chung, tôi nghĩ nó phụ thuộc vào phong cách viết mã của bạn. Tôi sẽ không trộn lẫn valueOf và "new", nếu cả hai đều có cùng kết quả.


7
Về mặt kỹ thuật thì đúng, nhưng : nó sẽ tạo ra sự khác biệt rất lớn . valueOf()có hành vi trực quan hơn , trong khi new BigDecimal(d)có hành vi chính xác hơn . Hãy thử cả hai và xem sự khác biệt.
Joachim Sauer

Sai về mặt kỹ thuật. Từ khóa 'new' luôn luôn tạo một đối tượng mới trong khi javadoc không cho biết giá trị của nó có trả về luôn một đối tượng mới hay không. Nó không, không phải luôn luôn. Nó có một số giá trị trong bộ nhớ cache để new BigDecimal(1) != new BigDecimal(1)nhưngBigDecimal.valueOf(1) == BigDecimal.valueOf(1)
aalku

1
@user: yes, nhưng vì BigDecimallà bất biến nó cần được điều trị cùng một cách mà những gói nguyên thủy ( Integer, Byte, ...) và Stringđược đối xử: nhận dạng đối tượng nên không quan trọng để mã của bạn, chỉ có giá trị nên quan trọng.
Joachim Sauer

@Joachim Đúng nhưng bộ nhớ cache bên trong đó là có lý do. Quá nhiều phiên bản BigDecimal bằng nhau không cần thiết không phải là điều tốt nên có. Và tôi đã trả lời cho Dr, Ông nói "một đối tượng mới sẽ được tạo ra"
aalku

3
@ người dùng: vâng, đó là lý do tại sao tôi nói rằng thườngvalueOf() nên được ưu tiên. Nhưng lưu ý rằng điều đó không thực hiện bất kỳ bộ nhớ đệm nào (và nó có thể sẽ không đáng giá). BigDecimal.valueOf(double)
Joachim Sauer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.