2015-02-26 16 views
8

Nếu bạn sử dụng BigInteger (hoặc BigDecimal) và muốn thực hiện số học trên chúng, bạn phải sử dụng các phương thức add hoặc subtract chẳng hạn. Này nghe có vẻ tốt cho đến khi bạn nhận ra rằng đâyTại sao không phải là BigInteger nguyên thủy

i += d + p + y; 

sẽ được viết như thế này cho một BigInteger:

i = i.add(d.add(p.add(y))); 

Như bạn có thể nhìn thấy nó là một chút dễ dàng hơn để đọc dòng đầu tiên. Điều này có thể được giải quyết nếu Java cho phép quá tải nhà điều hành nhưng nó không, vì vậy điều này đặt ra câu hỏi:

Tại sao không phải là BigInteger loại nguyên thủy để tận dụng lợi thế của các toán tử giống như các kiểu nguyên thủy khác?

+1

Câu hỏi này có thể phù hợp hơn với trang [lập trình viên] (http://programmers.stackexchange.com/). – GoBusto

+3

Câu hỏi của bạn tóm tắt thành * tại sao Java không hỗ trợ quá tải toán tử? * Đang đặt câu hỏi về thiết kế Java hiện tại, mà chúng tôi có thể hoặc không biết câu trả lời chính xác. –

+7

Nguyên thủy là các biến mà CPU có hỗ trợ để hoạt động trực tiếp, với 'BigInteger' không phải như vậy. Đây là một lớp hỗ trợ hoạt động với số lớn (thực sự lớn nếu bạn phải), và các hoạt động như vậy yêu cầu quản lý nhiều hơn đáng kể. Bởi vì một lớp của nó và vì Java không hỗ trợ quá tải toán tử, bạn được yêu cầu sử dụng các phương thức của nó thay vì các toán tử số học đơn giản mà bạn sử dụng với các nguyên thủy. – Havenard

Trả lời

16

Đó là bởi vì BigInteger là không, trên thực tế, mọi thứ gần để trở thành nguyên thủy. Nó được thực hiện bằng cách sử dụng một mảng và một số trường bổ sung, và các hoạt động khác nhau bao gồm các hoạt động phức tạp. Ví dụ, đây là việc thực hiện add:

public BigInteger add(BigInteger val) { 
    if (val.signum == 0) 
     return this; 
    if (signum == 0) 
     return val; 
    if (val.signum == signum) 
     return new BigInteger(add(mag, val.mag), signum); 

    int cmp = compareMagnitude(val); 
    if (cmp == 0) 
     return ZERO; 
    int[] resultMag = (cmp > 0 ? subtract(mag, val.mag) 
         : subtract(val.mag, mag)); 
    resultMag = trustedStripLeadingZeroInts(resultMag); 

    return new BigInteger(resultMag, cmp == signum ? 1 : -1); 
} 

Primitives trong Java là loại mà thường được thực hiện trực tiếp bởi CPU của máy chủ. Ví dụ: mọi máy tính hiện đại đều có hướng dẫn bằng ngôn ngữ máy để thêm vào số nguyên. Do đó nó cũng có thể có mã byte rất đơn giản trong JVM.

Một loại phức tạp như BigInteger thường không thể được xử lý theo cách đó và không thể dịch thành mã byte đơn giản. Nó không thể là nguyên thủy.


Vì vậy, câu hỏi của bạn có thể là "Tại sao không có toán tử quá tải trong Java". Vâng, đó là một phần của triết lý ngôn ngữ.


Và tại sao không tạo ngoại lệ, như cho String? Bởi vì nó không chỉ là một nhà điều hành đó là ngoại lệ.Bạn cần tạo ngoại lệ cho các toán tử *, /, +, -, <<, ^ và cứ tiếp tục như vậy. Và bạn vẫn sẽ có một số hoạt động trong chính đối tượng đó (như pow không được đại diện bởi một toán tử trong Java), mà đối với các nguyên thủy được xử lý bởi các lớp đặc biệt (như Math).

+0

Điểm tuyệt vời về bytecode. Đáng chú ý là trong các ngôn ngữ khác (như JavaScript), Strings được coi là nguyên thủy - khái niệm "nguyên thủy" khá run rẩy, và ở một mức độ nào đó phụ thuộc vào ngôn ngữ. –

+3

Mặc dù yêu cầu hỗ trợ trong các thư viện khác nhau, vẫn còn khả thi đối với BigInteger như một kiểu nguyên thủy. Tôi không thấy bất kỳ trở ngại kỹ thuật nào khi làm điều đó. Ngay cả khi thiếu sự hỗ trợ CPU không phải là một đối số: Bạn phải nhớ các CPU đầu tiên mà các hoạt động điểm trôi nổi được mô phỏng trong khi vẫn được hỗ trợ bởi các ngôn ngữ như C. – Tarik

+1

"Hãy nhớ rằng Chuỗi đã có một vị trí đặc biệt trong Đặc tả Ngôn ngữ Java" Điều gì là để ngăn chặn có cùng một vị trí đặc biệt cho BigInteger hoặc bất kỳ loại nào khác như số phức cho vấn đề đó? – Tarik

2

intbooleanchar không nguyên thủy để bạn có thể tận dụng lợi thế của các nhà khai thác như +/. Họ là nguyên thủy vì lý do lịch sử, lớn nhất trong số đó là hiệu suất.

Trong Java, nguyên thủy được định nghĩa là những thứ không phải là đối tượng chính thức. Tại sao tạo các cấu trúc bất thường này (và sau đó triển khai lại chúng dưới dạng đối tượng thích hợp, chẳng hạn như Integer, sau này)? Chủ yếu cho hiệu suất: hoạt động trên các đối tượng là (và) chậm hơn so với các hoạt động trên các kiểu nguyên thủy. (Như các câu trả lời khác đề cập đến, hỗ trợ phần cứng đã làm cho các hoạt động này nhanh hơn, nhưng tôi không đồng ý rằng hỗ trợ phần cứng là "tài sản thiết yếu" của nguyên thủy.)

Vì vậy một số loại đã được xử lý đặc biệt. và những người khác thì không. Hãy suy nghĩ về nó theo cách này: nếu ngay cả những người cực kỳ phổ biến String không phải là một loại nguyên thủy, tại sao BigInteger là?

+0

Chuỗi được coi là kiểu nguyên thủy trong Java. – Tarik

+3

@Tarik Không, nó [không] (http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) được coi là kiểu nguyên thủy. –

0

Loại nguyên thủy thường là các loại lịch sử được xác định bởi kiến ​​trúc bộ vi xử lý. Đó là lý do tại sao byte là 8-bit, ngắn là 16-bit, int là 32-bit và dài là 64-bit. Có lẽ khi có nhiều kiến ​​trúc 128 bit hơn, một nguyên thủy bổ sung sẽ được tạo ... nhưng tôi không thể thấy có đủ ổ đĩa cho điều này ...

+0

Hỗ trợ CPU không phải là điều kiện tiên quyết để có BigInteger như một kiểu nguyên thủy. Chuỗi là ở đây để chứng minh điều đó. – Tarik

+0

bạn nói đúng, tôi nghĩ tôi đã trả lời một câu hỏi khác với những gì đã được hỏi, cụ thể là "tại sao không còn nguyên thủy" từ pov lịch sử hơn là "tại sao không phải là BigInteger nguyên thủy" – mixmastered

+0

@Tarik Strings không phải là loại nguyên thủy, chúng chỉ có cách xử lý đặc biệt. nguồn: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html – DSquare

5

Về cơ bản, vì ý nghĩa phi chính thức của "nguyên thủy" là dữ liệu của nó có thể được xử lý trực tiếp với một đơn CPU instruction. Nói cách khác, chúng là nguyên thủy vì chúng phù hợp với từ 32 bit hoặc 64 bit, đó là kiến ​​trúc dữ liệu mà CPU của bạn làm việc với, vì vậy chúng có thể được lưu trữ một cách rõ ràng trong registers.

Và do đó CPU của bạn có thể làm cho các hoạt động sau:

ADD REGISTER_3 REGISTER_2 REGISTER_1  ;;; REGISTER_3 = REGISTER_1 + REGISTER_2 

Một BigInteger mà có thể chiếm một số lượng tùy ý lớn của bộ nhớ không thể được lưu trữ trong một REGISTER đơn và sẽ cần phải thực hiện nhiều hướng dẫn để thực hiện một khoản tiền đơn giản.

Đây là lý do tại sao chúng không thể là một kiểu nguyên thủy, và bây giờ chúng thực sự là đối tượng với phương thức và trường, cấu trúc phức tạp hơn nhiều so với các kiểu nguyên thủy đơn giản.

Lưu ý: Lý do tôi gọi đây là không chính thức là vì cuối cùng các nhà thiết kế Java có thể xác định "loại nguyên thủy Java" như bất kỳ thứ gì họ muốn, họ sở hữu từ đó, tuy nhiên điều này là mơ hồ khi sử dụng đồng ý từ đó.

+1

Hỗ trợ CPU không phải là điều kiện tiên quyết để có BigInteger như một kiểu nguyên thủy trong bất kỳ ngôn ngữ lập trình nào. – Tarik

+1

nguyên thủy là một từ chủ quan mà bất kỳ ai cũng có thể sử dụng theo ý muốn. Những gì tôi nói là thỏa thuận chung. Có ai đó có thể tạo một kiểu nguyên thủy không được CPU hỗ trợ. Các nhà thiết kế Java đã không, họ chỉ gọi nguyên thủy để hỗ trợ CPU các loại, như tôi đã nói với bạn Strings không * techinally nguyên thủy * (trích dẫn từ tài liệu) vì lý do này. Trong các ngôn ngữ khác, họ có thể xác định lại từ như họ muốn, không phải là điểm của câu hỏi Java này. – DSquare

1

Đó là vì các loại nguyên thủy có giới hạn kích thước. Ví dụ int là 32 bit và dài là 64 bit. Vì vậy, nếu bạn tạo một biến kiểu int thì JVM phân bổ 32 bit bộ nhớ trên ngăn xếp cho nó. Nhưng đối với BigInteger, nó "về mặt lý thuyết" không có giới hạn kích thước. Có nghĩa là nó có thể phát triển tùy ý về kích thước. Bởi vì điều này, không có cách nào để biết kích thước của nó và phân bổ một khối bộ nhớ cố định trên stack cho nó. Do đó nó được cấp phát trên heap nơi JVM luôn có thể tăng kích thước nếu cần thiết.

Các vấn đề liên quan