Tại sao Bộ sưu tập Java không thể lưu trữ trực tiếp các kiểu Nguyên thủy?


123

Bộ sưu tập Java chỉ lưu trữ các Đối tượng, không phải các kiểu nguyên thủy; tuy nhiên chúng ta có thể lưu trữ các lớp wrapper.

Tại sao lại có ràng buộc này?


2
Ràng buộc này thực sự tệ khi bạn xử lý các vấn đề sơ khai và muốn sử dụng Hàng đợi để gửi và tốc độ gửi của bạn rất nhanh. Tôi đang giải quyết vấn đề đó ngay bây giờ do autoboxing mất quá nhiều thời gian.
JPM

Về mặt kỹ thuật, các kiểu nguyên thủy là các đối tượng (chính xác là các trường hợp singleton), chúng chỉ không được định nghĩa bởi a class, đúng hơn là bởi JVM. Câu lệnh int i = 1xác định một con trỏ tới thể hiện singleton của đối tượng được định nghĩa inttrong JVM, được đặt thành giá trị 1được xác định ở đâu đó trong JVM. Có, con trỏ trong Java - điều này chỉ được tóm tắt với bạn bằng cách triển khai ngôn ngữ. Nguyên thủy không thể được sử dụng làm Generics vì ngôn ngữ biến tất cả các kiểu generic phải thuộc loại supertype Object- do đó tại sao A<?>phải biên dịch thành A<Object>tại thời điểm chạy.
Robert E Fry

1
@RobertEFry các kiểu nguyên thủy không phảiđối tượng trong Java, vì vậy mọi thứ bạn đã viết về các cá thể singleton và về cơ bản là sai và khó hiểu. Tôi khuyên bạn nên đọc chương "Kiểu, Giá trị và Biến" của Đặc tả ngôn ngữ Java, chương này định nghĩa đối tượng là gì: "Một đối tượng (§4.3.1) là một thể hiện được tạo động của một kiểu lớp hoặc một mảng được tạo động. "
typeracer 10/10 '19

Câu trả lời:


99

Đó là một quyết định thiết kế Java và một quyết định mà một số người coi là sai lầm. Vùng chứa muốn Đối tượng và nguyên thủy không bắt nguồn từ Đối tượng.

Đây là một nơi mà các nhà thiết kế .NET đã học hỏi từ JVM và triển khai các kiểu giá trị và số liệu chung để quyền anh bị loại bỏ trong nhiều trường hợp. Trong CLR, các vùng chứa chung có thể lưu trữ các kiểu giá trị như một phần của cấu trúc vùng chứa bên dưới.

Java đã chọn thêm hỗ trợ chung 100% trong trình biên dịch mà không cần hỗ trợ từ JVM. JVM là chính nó, không hỗ trợ một đối tượng "non-object". Java generics cho phép bạn giả vờ không có trình bao bọc, nhưng bạn vẫn phải trả giá bằng hiệu suất của quyền anh. Điều này là QUAN TRỌNG đối với một số lớp chương trình.

Quyền anh là một sự thỏa hiệp kỹ thuật, và tôi cảm thấy nó là chi tiết thực hiện bị rò rỉ sang ngôn ngữ. Autoboxing là một đường cú pháp tốt, nhưng vẫn là một hình phạt về hiệu suất. Nếu bất cứ điều gì, tôi muốn trình biên dịch cảnh báo tôi khi nó tự động đóng hộp. (Đối với tất cả những gì tôi biết, có thể bây giờ, tôi đã viết câu trả lời này vào năm 2010).

Một lời giải thích tốt trên SO về quyền anh: Tại sao một số ngôn ngữ cần Quyền anh và Mở hộp ?

Và những lời chỉ trích về các generic Java: Tại sao một số người lại cho rằng việc triển khai các generic của Java là không tốt?

Trong cách bảo vệ của Java, rất dễ bị nhìn lại và chỉ trích. JVM đã vượt qua thử thách của thời gian và là một thiết kế tốt ở nhiều khía cạnh.


6
Không phải là một sai lầm, một sự đánh đổi được lựa chọn cẩn thận mà tôi tin rằng nó thực sự đã phục vụ Java rất tốt.
DJClayworth

13
Đã đủ sai lầm mà .NET đã học được từ nó và triển khai autoboxing ngay từ đầu và các thông tin chung ở cấp VM mà không có chi phí quyền anh. Nỗ lực sửa lỗi của chính Java chỉ là một giải pháp cấp độ cú pháp vẫn bị ảnh hưởng bởi hiệu suất của autoboxing so với không có quyền anh nào cả. Việc triển khai Java đã cho thấy hiệu suất kém với cấu trúc dữ liệu lớn.
codenheim

2
@mrjoltcola: IMHO, tự động đóng hộp mặc định là một sai lầm, nhưng lẽ ra phải có cách để gắn thẻ các biến và tham số sẽ tự động đóng hộp các giá trị được cung cấp cho chúng. Ngay cả bây giờ, tôi nghĩ rằng nên có một phương tiện được thêm vào để chỉ định rằng một số biến hoặc tham số nhất định không được chấp nhận các giá trị mới được đóng hộp tự động [ví dụ: việc chuyển các Object.ReferenceEqualstham chiếu thuộc loại Objectxác định các số nguyên được đóng hộp là hợp pháp, nhưng nó không hợp pháp để truyền một giá trị nguyên]. Tính năng tự động mở hộp của Java là IMHO thật khó chịu.
supercat

18

Giúp việc thực hiện dễ dàng hơn. Vì các nguyên thủy Java không được coi là Đối tượng, bạn sẽ cần tạo một lớp bộ sưu tập riêng cho từng nguyên thủy này (không có mã mẫu để chia sẻ).

Tất nhiên, bạn có thể làm điều đó, chỉ cần xem GNU Trove , Apache Commons Primaries hoặc HPPC .

Trừ khi bạn có các bộ sưu tập thực sự lớn, chi phí cho các trình bao bọc không đủ quan trọng để mọi người quan tâm (và khi bạn có các bộ sưu tập nguyên thủy thực sự lớn, bạn có thể muốn dành nỗ lực để xem xét việc sử dụng / xây dựng cấu trúc dữ liệu chuyên biệt cho chúng ).


11

Đó là sự kết hợp của hai sự kiện:

  • Các kiểu nguyên thủy của Java không phải là kiểu tham chiếu (ví dụ: an intkhông phải là an Object)
  • Java thực hiện generics bằng cách sử dụng kiểu xóa các kiểu tham chiếu (ví dụ: a List<?>thực sự là a List<Object>tại thời điểm chạy)

Vì cả hai điều này đều đúng, các tập hợp Java chung không thể lưu trữ các kiểu nguyên thủy trực tiếp. Để thuận tiện, autoboxing được giới thiệu để cho phép các loại nguyên thủy được tự động đóng hộp như các loại tham chiếu. Đừng nhầm lẫn về điều đó, mặc dù vậy, các bộ sưu tập vẫn đang lưu trữ các tham chiếu đối tượng bất kể.

Điều này có thể tránh được không? Có lẽ.

  • Nếu an intlà một Object, thì không cần đến các loại hộp.
  • Nếu generic không được thực hiện bằng cách sử dụng type-delete, thì các phần tử nguyên thủy có thể đã được sử dụng cho các tham số kiểu.

8

Có khái niệm về auto-boxing và auto-unboxing. Nếu bạn cố gắng lưu trữ một inttrong một List<Integer>trình biên dịch Java sẽ tự động chuyển đổi nó thành một Integer.


1
Autoboxing đã được giới thiệu trong Java 1.5 cùng với Generics.
Jeremy

1
Nhưng nó là một thứ thời gian biên dịch; đường cú pháp mà không có lợi ích về hiệu suất. Các hộp tự động của trình biên dịch Java, do đó hình phạt về hiệu suất so với các triển khai VM như .NET, những người chung chung không liên quan đến quyền anh.
codenheim

1
@mrjoltcola: Ý của bạn là gì? Tôi chỉ đang chia sẻ sự thật, không đưa ra tranh luận.
Jeremy

3
Quan điểm của tôi là chỉ ra sự khác biệt giữa cú pháp và hiệu suất là rất quan trọng. Tôi cũng coi những bình luận của mình là chia sẻ sự thật chứ không phải tranh luận. Cảm ơn.
codenheim

2

Nó không thực sự là một hạn chế phải không?

Cân nhắc xem bạn có muốn tạo một bộ sưu tập lưu trữ các giá trị nguyên thủy hay không. Làm thế nào bạn sẽ viết một bộ sưu tập có thể lưu trữ int, hoặc float hoặc char? Nhiều khả năng bạn sẽ có nhiều bộ sưu tập, vì vậy bạn sẽ cần một danh sách giới thiệu và một danh sách biểu đồ, v.v.

Tận dụng tính chất hướng đối tượng của Java khi bạn viết một lớp tập hợp, nó có thể lưu trữ bất kỳ đối tượng nào nên bạn chỉ cần một lớp tập hợp. Ý tưởng này, tính đa hình, rất mạnh mẽ và đơn giản hóa việc thiết kế các thư viện.


7
"Làm thế nào bạn sẽ viết một bộ sưu tập có thể lưu trữ int, hoặc float hoặc char?" - Với các generics / khuôn mẫu được triển khai đúng cách như các ngôn ngữ khác không bị phạt khi giả vờ mọi thứ là một đối tượng.
codenheim

Tôi chưa bao giờ trong sáu năm sử dụng Java muốn lưu trữ một bộ sưu tập các nguyên thủy. Ngay cả trong một số ít trường hợp, tôi có thể muốn chi phí thêm về thời gian và không gian của việc sử dụng các đối tượng tham chiếu là không đáng kể. Đặc biệt, tôi thấy rằng nhiều người nghĩ rằng họ muốn Map <int, T>, mà quên rằng một mảng sẽ thực hiện thủ thuật đó rất độc đáo.
DJClayworth

2
@DJClayworth Điều đó chỉ hoạt động tốt nếu các giá trị khóa không thưa thớt. Tất nhiên, bạn có thể sử dụng một mảng phụ để theo dõi các khóa, nhưng điều đó có vấn đề riêng: truy cập tương đối hiệu quả sẽ yêu cầu giữ cả hai mảng được sắp xếp dựa trên thứ tự khóa để cho phép tìm kiếm nhị phân, do đó sẽ thực hiện việc chèn và xóa không hiệu quả trừ khi việc chèn / xóa theo khuôn mẫu để các mục được chèn có khả năng kết thúc ở vị trí của mục đã xóa trước đó và / hoặc một số vùng đệm nằm xen kẽ trong các mảng, v.v. Có sẵn các tài nguyên, nhưng sẽ rất tuyệt nếu có Bản thân Java.
JAB

@JAB Trên thực tế, nó hoạt động tốt nếu các phím thưa thớt, nó chỉ cần nhiều bộ nhớ hơn nếu chúng không thưa thớt. Và nếu các khóa của chúng thưa thớt, điều đó có nghĩa là không có nhiều trong số chúng và việc sử dụng Số nguyên làm khóa hoạt động tốt. Sử dụng cách tiếp cận nào cần ít bộ nhớ nhất. Hoặc bất cứ điều gì bạn cảm thấy thích nếu bạn không quan tâm.
DJClayworth

0

Tôi nghĩ rằng chúng ta có thể thấy tiến bộ trong không gian này trong JDK có thể trong Java 10 dựa trên JEP này - http://openjdk.java.net/jeps/218 .

Nếu bạn muốn tránh nguyên bản quyền anh trong các bộ sưu tập ngày nay, có một số lựa chọn thay thế của bên thứ ba. Ngoài các tùy chọn của bên thứ ba đã đề cập trước đó, còn có Bộ sưu tập Eclipse , FastUtilKoloboke .

Một so sánh các bản đồ nguyên thủy cũng đã được xuất bản cách đây ít lâu với tiêu đề: Tổng quan về bản đồ lớn HashMap: JDK, FastUtil, Goldman Sachs, HPPC, Koloboke, Trove . Thư viện GS Collections (Goldman Sachs) đã được chuyển sang Eclipse Foundation và bây giờ là Eclipse Collections.


0

Lý do chính là chiến lược thiết kế java. ++ 1) các bộ sưu tập yêu cầu các đối tượng để thao tác và các nguyên thủy không bắt nguồn từ đối tượng, vì vậy đây có thể là lý do khác. 2) Các kiểu dữ liệu nguyên thủy của Java không phải là kiểu tham chiếu cho ex. int không phải là một đối tượng.

Vượt qua:-

chúng tôi có khái niệm về tự động đấm bốc và tự động mở hộp. vì vậy nếu bạn đang cố gắng lưu trữ các kiểu dữ liệu nguyên thủy, trình biên dịch sẽ tự động chuyển đổi đó thành đối tượng của lớp dữ liệu nguyên thủy đó.

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.