Trong Java ArrayList<E>
cơ sở triển khai trên một mảng đối tượng.
Ai đó có thể giải thích cho tôi tại sao việc triển khai ArrayList<E>
sử dụng mảng Object[]
để lưu trữ dữ liệu thay vì E[]
? Lợi ích của việc sử dụng Object[]
là gì?Tại sao việc triển khai ArrayList sử dụng Object []?
Trả lời
Trong Java, việc tạo một mảng kiểu chung không đơn giản.
Phương pháp đơn giản không biên dịch:
public class Container<E> {
E[] arr = new E[3]; // ERROR: Cannot create a generic array of E
}
Thay E
với Object
, và tất cả là tốt (tại các chi phí của thêm phức tạp ở những nơi khác trong việc thực hiện container).
Có các cách tiếp cận khác, nhưng chúng trình bày một loạt các sự cân bằng khác nhau. Đối với một cuộc thảo luận sâu rộng, xem How to create a generic array in Java?
Xét type erasure (có nghĩa là, thực tế là Generics loại thông số như E
trong ví dụ của bạn được xóa tại loại biên soạn), tôi nghi ngờ các bytecode tạo ra sẽ tương tự trong cả hai trường hợp.
Từ quan điểm bảo trì, sử dụng thông số loại thay vì Object sẽ dẫn đến việc đọc mã dễ dàng hơn (vì nó sẽ giới hạn phôi). Nhưng vì API của ArrayList
không bao giờ cho thấy mảng "thô" Object
, tôi cho rằng nó không tạo ra bất kỳ sự khác biệt nào đối với các nhà phát triển Java đơn thuần của chúng tôi :)
Trước tiên, hãy nhận ra rằng kiểu thời gian thực tế của đối tượng mảng phải là Object[]
. Điều này là do các mảng biết kiểu thành phần của chúng trong thời gian chạy (các kiểu mảng khác nhau thực sự là các kiểu khác nhau trong thời gian chạy), và do đó bạn cần xác định kiểu thành phần trong việc tạo mảng, nhưng đối tượng ArrayList
không biết đối số kiểu của nó khi chạy.
Điều đó nói rằng, loại thời gian biên dịch của biến Ví dụ có thể được khai báo là một trong hai Object[]
hoặc E[]
, với những ưu điểm và nhược điểm khác nhau:
Nếu nó được khai báo là Object[]
:
private Object[] arr;
// to create it:
arr = new Object[3];
// to get an element:
E get(int i) { return (E)arr[i]; }
Các bất lợi của điều này là bạn phải đúc nó để E
mỗi khi bạn lấy một cái gì đó ra khỏi nó, có nghĩa là bạn về cơ bản sử dụng nó như là một container thế hệ trước.
Nếu nó được khai báo là E[]
:
private E[] arr;
// to create it:
arr = (E[])new Object[3];
// to get an element:
E get(int i) { return arr[i]; }
Ưu điểm của việc này là bạn không còn phải bỏ khi bạn nhận được điều ra khỏi nó - nó cung cấp loại kiểm tra về việc sử dụng của arr
, giống như các thùng chứa chung. Bất lợi là, một cách hợp lý, diễn viên là nói dối - chúng tôi biết chúng tôi tạo ra một đối tượng có kiểu thời gian chạy là Object[]
, và vì vậy nó không phải là một ví dụ của E[]
, trừ khi E
là Object
.
Tuy nhiên, không có vấn đề ngay lập tức khi thực hiện việc này, vì E
bị xóa thành Object
bên trong các phương pháp thể hiện của lớp học.Cách duy nhất một vấn đề có thể xảy ra là nếu đối tượng bằng cách nào đó tiếp xúc với bên ngoài của lớp (ví dụ như được trả về trong một phương thức, đặt trong trường công khai, v.v.) với công suất sử dụng loại của nó là E[]
(không phải) :
// This would be bad. It would cause a class cast exception at the call site
E[] getArray() { return arr; }
Nhưng ArrayList
, và thực sự bất kỳ lớp container đúng thiết kế tốt, sẽ không bao giờ tiếp xúc với một chi tiết thực hiện như mảng nội bộ của mình ra bên ngoài. Nó sẽ phá vỡ trừu tượng, trong số những thứ khác. Vì vậy, miễn là tác giả của lớp này nhận thức được không bao giờ phơi bày mảng này, không có vấn đề gì khi thực hiện theo cách này (có thể tiết kiệm có thể gây nhầm lẫn cho người tiếp theo nhìn thấy mã và không biết về nó), và miễn phí lợi thế của việc kiểm tra kiểu tăng lên theo cách này mang lại.
- 1. Việc triển khai Danh sách nào để sử dụng?
- 2. Tại sao việc triển khai izip() này không hoạt động?
- 3. Tại sao ArrayList sử dụng bộ nhớ tạm thời?
- 4. Tại sao việc triển khai ứng dụng khách websocket hiện tại không hỗ trợ proxy?
- 5. Tại sao HttpServlet triển khai Serializable?
- 6. Tại sao việc sử dụng Object Initializer giữ một đối tượng còn sống?
- 7. Tại sao HttpSessionState không triển khai IDictionary?
- 8. Cách tìm bản sao trong một ArrayList <Object>?
- 9. Tại sao việc triển khai Externalizable lại cần một hàm tạo công khai mặc định?
- 10. Lịch.before (Object when), tại sao Object?
- 11. Arrays.sort (Object [] a) - được triển khai như thế nào?
- 12. Tại sao Enumerable.Range triển khai IDisposable?
- 13. Tại sao việc triển khai giao diện chung này tạo ra một tham chiếu mơ hồ?
- 14. ArrayList vs Danh sách <object>
- 15. Tại sao việc triển khai CSP chỉ bao gồm các kênh?
- 16. Bằng chứng: tại sao việc triển khai java.lang.String.hashCode() khớp với tài liệu của nó?
- 17. Tại sao JavaScript được triển khai bằng cách sử dụng thừa kế prototypal?
- 18. Tại sao danh sách Giao diện không thể sử dụng loại triển khai?
- 19. Tại sao ID của việc triển khai OpenID của Google thay đổi?
- 20. Việc triển khai các ứng dụng .NET so với việc triển khai các ứng dụng web Java như thế nào?
- 21. Tại sao việc ghi đè loại trừu tượng đã triển khai không thể thực hiện được?
- 22. Tại sao ArrayList thực hiện IList, ICollection, IEnumerable?
- 23. Tại sao triển khai giao diện rõ ràng?
- 24. Tại sao nhà phát triển sử dụng Silverlight?
- 25. Tại sao các loại lớp khó triển khai?
- 26. Tại sao sử dụng macro trong khai báo lớp
- 27. Tại sao bỏ null vào Object?
- 28. Tại sao gán một ArrayList mới vào một biến List?
- 29. Tại sao Hadoop không triển khai bằng MPI?
- 30. Tại sao ValueType.GetHashCode() được triển khai giống như vậy?
Bạn đã đúng! Cảm ơn bạn!!! – user485553
Bạn vẫn có thể thực hiện một phép chọn không được kiểm soát 'E [] arr = (E []) đối tượng mới [3]; ' – Saintali
@Saintali: Bạn có thể, nhưng bạn sẽ nhận được một' ClassCastException' thời điểm bạn sẽ cố gắng làm việc với mảng này. Thử nó. – NPE