Tại sao API Java sử dụng int thay vì ngắn hoặc byte?


137

Tại sao API Java sử dụng int, khi nào shorthoặc thậm chí bytelà đủ?

Ví dụ: DAY_OF_WEEKTrường trong lớp Calendarsử dụng int.

Nếu sự khác biệt quá nhỏ, thì tại sao những kiểu dữ liệu đó ( short, int) lại tồn tại?

Câu trả lời:


166

Một số lý do đã được chỉ ra. Ví dụ, thực tế là "... (Hầu hết) Tất cả các hoạt động trên byte, short sẽ thúc đẩy các nguyên hàm này thành int" . Tuy nhiên, câu hỏi tiếp theo rõ ràng sẽ là: TẠI SAO các loại này được quảng bá int?

Vì vậy, để đi sâu hơn một cấp: Câu trả lời có thể chỉ đơn giản là liên quan đến Bộ hướng dẫn máy ảo Java. Như tóm tắt trong Bảng trong Java Virtual Machine Đặc điểm kỹ thuật , tất cả các phép tính số học không thể thiếu, như thêm, chia và những người khác, chỉ có sẵn cho các loại intvà các loại long, và không cho các loại nhỏ hơn.

(Bỏ qua một bên: Các loại nhỏ hơn ( byteshort) về cơ bản chỉ dành cho các mảng . Một mảng như new byte[1000]sẽ lấy 1000 byte và một mảng như thế new int[1000]sẽ mất 4000 byte)

Bây giờ, tất nhiên, người ta có thể nói rằng "... câu hỏi tiếp theo rõ ràng sẽ là: TẠI SAO những hướng dẫn này chỉ được cung cấp cho int(và long)?" .

Một lý do được đề cập trong Spec JVM được đề cập ở trên:

Nếu mỗi lệnh được nhập hỗ trợ tất cả các kiểu dữ liệu thời gian chạy của Máy ảo Java, sẽ có nhiều hướng dẫn hơn mức có thể được biểu thị trong một byte

Ngoài ra, Máy ảo Java có thể được coi là một bản tóm tắt của bộ xử lý thực. Và giới thiệu Đơn vị logic số học chuyên dụng cho các loại nhỏ hơn sẽ không đáng nỗ lực: Nó sẽ cần thêm các bóng bán dẫn, nhưng nó vẫn chỉ có thể thực hiện một phép cộng trong một chu kỳ đồng hồ. Kiến trúc thống trị khi JVM được thiết kế là 32 bit, vừa phải cho 32 bit int. (Các hoạt động liên quan đến longgiá trị 64 bit được thực hiện như một trường hợp đặc biệt).

(Lưu ý: Đoạn cuối hơi quá đơn giản, xem xét khả năng vector hóa có thể, v.v., nhưng nên đưa ra ý tưởng cơ bản mà không đi sâu vào các chủ đề thiết kế bộ xử lý)


EDIT: Một phụ lục ngắn, tập trung vào ví dụ từ câu hỏi, nhưng theo nghĩa chung hơn: Người ta cũng có thể hỏi liệu có hữu ích khi lưu trữ các trường bằng cách sử dụng các loại nhỏ hơn không. Ví dụ, người ta có thể nghĩ rằng bộ nhớ có thể được lưu bằng cách lưu trữ Calendar.DAY_OF_WEEKdưới dạng a byte. Nhưng ở đây, Định dạng tệp lớp Java xuất hiện: Tất cả các trường trong tệp lớp chiếm ít nhất một "khe", có kích thước bằng một int(32 bit). (Các trường "rộng" doublelong, chiếm hai vị trí). Vì vậy, tuyên bố rõ ràng một trường là shorthoặc bytesẽ không lưu bất kỳ bộ nhớ.


Tôi đoán rằng logic tại sao toán hạng được thăng cấp lên int cũng liên quan đến lý do được sử dụng trong C và C ++
Shafik Yaghmour

@ Marco13 "Vì vậy, tuyên bố rõ ràng một trường là ngắn hoặc byte cũng sẽ không lưu bất kỳ bộ nhớ nào." điều đó có đúng không Tôi không nghĩ đó là chính xác.
ACV

@ACV Nói đúng ra, một triển khai có thể chọn lưu trữ một hình thức nhỏ gọn hơn, nhưng định dạng được phơi bày "hầu như" (tức là bởi máy ảo) sẽ coi các giá trị có kích thước tối thiểu bằng int. Nếu bạn có một tham chiếu đến một triển khai khác, tôi sẽ cập nhật câu trả lời và chèn liên kết tương ứng.
Marco13

40

(Hầu hết) Tất cả các hoạt động trên byte, shortsẽ thúc đẩy chúng int, ví dụ, bạn không thể viết:

short x = 1;
short y = 2;

short z = x + y; //error

Arithologists dễ dàng và đơn giản hơn khi sử dụng int, không cần phải đúc.

Về mặt không gian, nó tạo ra một sự khác biệt rất nhỏ. byteshortsẽ làm phức tạp mọi thứ, tôi không nghĩ rằng tối ưu hóa vi mô này đáng giá vì chúng ta đang nói về một lượng biến cố định.

bytecó liên quan và hữu ích khi bạn lập trình cho các thiết bị nhúng hoặc xử lý các tệp / mạng. Ngoài ra những nguyên thủy này còn hạn chế, nếu tính toán có thể vượt quá giới hạn của chúng trong tương lai thì sao? Hãy thử nghĩ về một phần mở rộng cho Calendarlớp có thể phát triển số lượng lớn hơn.

Cũng lưu ý rằng trong một bộ vi xử lý 64-bit, người dân địa phương sẽ được lưu trong thanh ghi và sẽ không sử dụng bất kỳ tài nguyên, vì vậy sử dụng int, shortvà nguyên thủy khác sẽ không thực hiện bất kỳ khác biệt nào cả. Hơn nữa, nhiều triển khai Java sắp xếp các biến * (và các đối tượng).


* byteshortchiếm không gian giống như intnếu họ là địa phương biến, lớp biến hoặc thậm chí dụ biến. Tại sao? Bởi vì trong (hầu hết) các hệ thống máy tính, các địa chỉ biến được căn chỉnh , vì vậy, ví dụ nếu bạn sử dụng một byte đơn, thực tế bạn sẽ kết thúc với hai byte - một cho chính biến đó và một cho đệm.

Mặt khác, trong các mảng, bytelấy 1 byte, shortlấy 2 byte và intlấy bốn byte, bởi vì trong mảng chỉ có phần đầu và có thể phần cuối của nó phải được căn chỉnh. Điều này sẽ tạo ra sự khác biệt trong trường hợp bạn muốn sử dụng, ví dụ System.arraycopy(), sau đó bạn sẽ thực sự ghi nhận sự khác biệt về hiệu suất.


1
Sự thật thú vị: Nếu bạn sử dụng công cụ sửa đổi cuối cùng cho cả hai giá trị, nó sẽ hoạt động. :)
alexander

7

Bởi vì các phép toán số học dễ dàng hơn khi sử dụng số nguyên so với quần short. Giả sử rằng các hằng số thực sự được mô hình hóa bằng shortcác giá trị. Sau đó, bạn sẽ phải sử dụng API theo cách này:

short month = Calendar.JUNE;
month = month + (short) 1; // is july

Chú ý đúc rõ ràng. Các giá trị ngắn được ngầm phát huy thành intcác giá trị khi chúng được sử dụng trong các phép toán số học. (Trên ngăn toán hạng, quần short thậm chí được biểu thị bằng số nguyên.) Điều này sẽ khá cồng kềnh khi sử dụng, đó là lý do tại sao intcác giá trị thường được ưa thích cho các hằng số.

So với điều đó, mức tăng hiệu quả lưu trữ là tối thiểu vì chỉ tồn tại một số lượng cố định như vậy. Chúng ta đang nói về 40 hằng số. Thay đổi lưu trữ của họ từ intđể shortWould an toàn bạn 40 * 16 bit = 80 byte. Xem câu trả lời này để tham khảo thêm.


5

Nếu bạn đã sử dụng triết lý trong đó các hằng số tích phân được lưu trữ theo loại nhỏ nhất mà chúng phù hợp, thì Java sẽ có một vấn đề nghiêm trọng: bất cứ khi nào lập trình viên viết mã bằng các hằng số tích phân, họ phải chú ý cẩn thận đến mã của họ để kiểm tra xem loại các hằng số quan trọng, và nếu vậy hãy tra cứu loại trong tài liệu và / hoặc thực hiện bất kỳ chuyển đổi loại nào là cần thiết.

Vì vậy, bây giờ chúng tôi đã vạch ra một vấn đề nghiêm trọng, bạn có thể hy vọng đạt được những lợi ích gì với triết lý đó? Tôi sẽ không ngạc nhiên nếu hiệu ứng duy nhất có thể quan sát được của sự thay đổi đó sẽ là loại bạn nhận được khi bạn nhìn lên hằng số thông qua sự phản chiếu. (và, tất nhiên, bất kỳ lỗi nào được đưa ra bởi các lập trình viên lười biếng / không mong muốn không hạch toán chính xác các loại hằng số)

Cân nhắc ưu và nhược điểm là rất dễ: đó là một triết lý tồi.


4

Sự phức tạp trong thiết kế của một máy ảo là một chức năng có bao nhiêu loại hoạt động mà nó có thể thực hiện. Thật dễ dàng hơn khi có bốn triển khai một lệnh như "nhân" - mỗi lần cho số nguyên 32 bit, số nguyên 64 bit, dấu phẩy động 32 bit và dấu phẩy động 64 bit - ngoài ra còn có ở trên, các phiên bản cho các loại số nhỏ hơn là tốt. Một câu hỏi thiết kế thú vị hơn là tại sao nên có bốn loại, thay vì ít hơn (thực hiện tất cả các tính toán số nguyên với số nguyên 64 bit và / hoặc thực hiện tất cả các tính toán dấu phẩy động với các giá trị dấu phẩy động 64 bit). Lý do sử dụng số nguyên 32 bit là Java dự kiến ​​sẽ chạy trên nhiều nền tảng trong đó các loại 32 bit có thể được xử lý nhanh như các loại 16 bit hoặc 8 bit, nhưng các hoạt động trên các loại 64 bit sẽ đáng chú ý Chậm hơn.chỉ có loại 32 bit.

Đối với việc thực hiện tính toán dấu phẩy động trên các giá trị 32 bit, các ưu điểm hơi rõ ràng hơn một chút. Có một số nền tảng trong đó tính toán nhưfloat a=b+c+d;có thể được thực hiện nhanh nhất bằng cách chuyển đổi tất cả các toán hạng thành loại có độ chính xác cao hơn, thêm chúng và sau đó chuyển đổi kết quả trở lại thành số dấu phẩy động 32 bit để lưu trữ. Có những nền tảng khác sẽ hiệu quả hơn khi thực hiện tất cả các tính toán bằng cách sử dụng các giá trị dấu phẩy động 32 bit. Những người tạo ra Java đã quyết định rằng tất cả các nền tảng nên được yêu cầu thực hiện theo cùng một cách và họ nên ưu tiên các nền tảng phần cứng để tính toán dấu phẩy động 32 bit nhanh hơn các nền tảng dài hơn, mặc dù PC này bị suy giảm nghiêm trọng cả về tốc độ và độ chính xác của toán học dấu phẩy động trên một PC điển hình, cũng như trên nhiều máy không có đơn vị dấu phẩy động. Lưu ý, btw, tùy thuộc vào các giá trị của b, c và d, sử dụng các tính toán trung gian có độ chính xác cao hơn khi tính toán các biểu thức như đã nói ở trênfloat a=b+c+d;đôi khi sẽ mang lại kết quả chính xác hơn đáng kể so với tất cả các toán hạng trung gian được tính toán floatchính xác, nhưng đôi khi sẽ mang lại một giá trị ít chính xác hơn một chút. Trong mọi trường hợp, Sun quyết định mọi thứ nên được thực hiện theo cùng một cách và họ đã chọn sử dụng các floatgiá trị độ chính xác tối thiểu .

Lưu ý rằng các ưu điểm chính của các loại dữ liệu nhỏ hơn trở nên rõ ràng khi số lượng lớn chúng được lưu trữ cùng nhau trong một mảng; ngay cả khi không có lợi thế nào khi có các biến riêng lẻ có loại nhỏ hơn 64 bit, thì đáng để có các mảng có thể lưu trữ các giá trị nhỏ gọn hơn; có một biến cục bộ là một bytethay vì longtiết kiệm bảy byte; có một mảng 1.000.000 số giữ mỗi số bytethay vì mộtlongsóng 7.000.000 byte. Vì mỗi loại mảng chỉ cần hỗ trợ một vài thao tác (đáng chú ý nhất là đọc một mục, lưu trữ một mục, sao chép một phạm vi các mục trong một mảng hoặc sao chép một phạm vi các mục từ mảng này sang mảng khác), độ phức tạp thêm của việc có nhiều hơn các kiểu mảng không nghiêm trọng bằng sự phức tạp của việc có nhiều loại giá trị số rời rạc có thể sử dụng trực tiếp hơn.


2

Trên thực tế, có một lợi thế nhỏ. Nếu bạn có một

class MyTimeAndDayOfWeek {
    byte dayOfWeek;
    byte hour;
    byte minute;
    byte second;
}

sau đó trên một JVM điển hình, nó cần nhiều không gian như một lớp chứa một đơn int. Mức tiêu thụ bộ nhớ được làm tròn thành bội số 8 hoặc 16 byte tiếp theo (IIRC, có thể định cấu hình được), vì vậy các trường hợp khi có lưu thực sự là khá hiếm.

Lớp này sẽ dễ sử dụng hơn một chút nếu các Calendarphương thức tương ứng trả về a byte. Nhưng không có Calendarphương thức nào như vậy , chỉ có phương thức get(int)phải trả về intvì các trường khác. Mỗi thao tác trên các loại nhỏ hơn đều khuyến khích int, vì vậy bạn cần đúc rất nhiều.

Rất có thể, bạn sẽ từ bỏ và chuyển sang một inthoặc viết setters như

void setDayOfWeek(int dayOfWeek) {
    this.dayOfWeek = checkedCastToByte(dayOfWeek);
}

Sau đó, loại DAY_OF_WEEKkhông quan trọng.

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.