Tại sao java.util.ArrayList cho phép thêm null?


41

Tôi tự hỏi tại sao java.util.ArrayListcho phép thêm null. Có trường hợp nào tôi muốn thêm nullvào ArrayListkhông?

Tôi đang hỏi câu hỏi này bởi vì trong một dự án, chúng tôi đã có một lỗi trong đó một số mã được thêm nullvào ArrayListvà thật khó để phát hiện ra lỗi đó ở đâu. Rõ ràng là a NullPointerExceptionđã bị ném nhưng cho đến khi mã khác cố gắng truy cập vào phần tử. Vấn đề là làm thế nào để xác định vị trí mã đã thêm nullđối tượng. Sẽ dễ dàng hơn nếu ArrayListném một ngoại lệ trong mã nơi các phần tử được thêm vào.


12
Các dự án Ổi có một trang khá thú vị về chủ đề đó (họ không cho phép nullở hầu hết các bộ sưu tập của họ).
Joachim Sauer

6
Tôi tin rằng các câu trả lời được đưa ra ở đây bao gồm tốt câu hỏi. Có lẽ một điều nên được đề cập là: đừng coi mọi thứ trong JDK là hoàn hảo và hoàn hảo, sau đó đánh vào đầu bạn để cố gắng hiểu tại sao nó lại "hoàn hảo" như vậy. Một số điều là (trung thực, IMHO), vẫn còn đó do khả năng tương thích ngược và đó là điều đó. Ngay cả những người sáng tạo Java cũng thừa nhận điều đó, chỉ cần đọc sách của Joshua Bloch để thấy sự phê phán của anh ta về các API Java nhất định. Ở mức độ nào, câu hỏi của bạn liên quan đến thời tiết không có cách nào hay hơn để bắt NPE trong Java. Câu trả lời là, không, nhưng nên có.
Shivan Dragon

2
Bạn có thể cung cấp thêm thông tin về lý do tại sao nó không nên được cho phép? Nếu nó chỉ là một vấn đề của hương vị, thì nên ít hạn chế hơn.
Mare Infinitus

Câu trả lời đơn giản chỉ nulllà cách mặc định để biểu thị dữ liệu bị thiếu trong Java, cho dù bạn có thích hay không. Trong thực tế, rất nhiều người không thích nó và biến nó thành một đối số cho những thứ theo kiểu lập trình chức năng. Câu trả lời được đánh giá cao nhất không có ý nghĩa / không nắm bắt được bản chất của vấn đề.
xji

@JIXiang Bạn đang quá đơn giản hóa những gì null. "Mất tích" chỉ là một trong một số cách giải thích tiềm năng null. Các cách hiểu hợp lệ khác có thể là "Không xác định", "Không áp dụng" hoặc "Chưa được khởi tạo". Những gì nullđại diện phụ thuộc vào ứng dụng. Như cộng đồng Python sẽ nói: "Trước sự mơ hồ, hãy từ chối sự cám dỗ để đoán." Từ chối giữ null trong một container hoàn toàn có khả năng làm như vậy sẽ chỉ là như vậy - một phỏng đoán.
riwalk

Câu trả lời:


34

Quyết định thiết kế này xuất hiện chủ yếu được thúc đẩy bởi việc đặt tên.

Tên ArrayList gợi ý cho người đọc một chức năng tương tự như mảng - và việc các nhà thiết kế Khung sưu tập Java hy vọng rằng phần lớn người dùng API sẽ dựa vào chức năng tương tự như mảng.

Điều này đặc biệt, liên quan đến việc điều trị các yếu tố null. Người dùng API biết rằng bên dưới hoạt động tốt:

array[0] = null; // NPE won't happen here

sẽ khá ngạc nhiên khi tìm hiểu xem mã tương tự cho ArrayList có ném NPE không:

arrayList.set(0, null); // NPE => WTF?

Lý do như trên được trình bày trong các điểm nhấn mạnh hướng dẫn của JCF cho thấy sự tương đồng gần gũi giữa ArrayList và mảng đơn giản:

ArrayList ... cung cấp truy cập vị trí theo thời gian liên tục và chỉ đơn giản là nhanh chóng ...

Nếu bạn muốn triển khai Danh sách không cho phép null, tốt hơn là gọi như thế NonNullableArrayListhoặc đại loại như thế, để tránh gây nhầm lẫn cho người dùng API.


Lưu ý bên có một cuộc thảo luận phụ trợ trong các bình luận bên dưới, cùng với những cân nhắc bổ sung hỗ trợ cho lý luận được nêu ra ở đây.


12
Giải thích này không thuyết phục, xem xét LinkedList cũng hỗ trợ nullcác mục danh sách.
Stephen C

12
Phải ... nhưng một lời giải thích hợp lý hơn và đơn giản hơn (IMO) là cho phép nullcác mục nhập hữu ích trong nhiều trường hợp.
Stephen C

7
ArrayList không được gọi như vậy bởi vì nó bắt chước một mảng. Nó được gọi như vậy bởi vì nó là một danh sách được thực hiện dưới dạng một mảng. Giống như TreeMap không hoạt động như Cây.
Florian F

11
Câu trả lời này, IMO, hoàn toàn sai. Nulls được phép vì trong Java, tốt hơn hoặc xấu hơn, (IMO, tệ hơn) null được sử dụng rất nhiều để thể hiện các giá trị chưa được khởi tạo hoặc thiếu. Làm thế nào mà nó nhận được nhiều phiếu bầu nhất và kiểm tra "chấp nhận"? Nghiêm túc mà nói, tôi thực sự đang mất niềm tin vào StackOverflow những ngày này.
user949300

8
Câu trả lời này hoàn toàn sai. Đây chỉ là phỏng đoán không được hỗ trợ về null được cho phép trong việc thực hiện danh sách. Đây là một roundup của bộ sưu tập Java cho phép / không cho phép null; chú ý làm thế nào nó không có gì để làm với sự tương tự với mảng.
Andres F.

32

Null có thể là một giá trị hợp lệ cho một thành phần của danh sách. Giả sử danh sách của bạn chứa các thành phần đại diện cho một số dữ liệu tùy chọn về danh sách người dùng và được lưu trữ theo cùng thứ tự với người dùng. Nếu dữ liệu bổ sung được điền thì danh sách của bạn sẽ chứa dữ liệu bổ sung nếu không thì vị trí tương ứng với người dùng sẽ là null. (Tôi chắc chắn có những ví dụ tốt hơn, nhưng bạn hiểu ý)

nếu bạn không muốn cho phép null được thêm vào thì bạn có thể bọc danh sách mảng bằng trình bao bọc của riêng bạn, nó được ném khi null được thêm vào.


2
Đó dường như là một ví dụ tồi về lý do tại sao nên cho phép null - có những cách biểu thị dữ liệu tùy chọn tốt hơn so với sử dụng giá trị null trong danh sách.
casifer

@casTHER yeah hoàn toàn đồng ý, nó không phải là một ví dụ tuyệt vời.
Người giữ Sam

Tôi không thể tìm thấy lý do chính đáng để ArrayList chứa null, những bất ngờ xấu khác để nhà phát triển tiếp theo 'khám phá' nó: /
AndreasScheinert

7
@casTHER Mặc dù tôi đồng ý với bạn rằng nên tránh null để thể hiện dữ liệu tùy chọn, trong Java, tốt hơn hoặc xấu hơn, đó là "truyền thống".
user949300

2
Trường hợp sử dụng vững chắc: Bạn muốn truyền tham số cho một số hàm động dưới dạng danh sách. Bạn có một số hàm, mỗi hàm có một bộ tham số khác nhau, vì vậy bạn cần một mảng có kích thước động và bạn luôn có thể chuyển null thành đối số hàm hợp lệ!
Falco

19

ArrayListcho phép null theo thiết kế. Đó là cố ý. Từ javadoc :

"[ArrayList là một] triển khai mảng có thể thay đổi kích thước của giao diện Danh sách. Triển khai tất cả các hoạt động danh sách tùy chọn và cho phép tất cả các thành phần, bao gồm null ."

Câu trả lời cho "tại sao" là nếu ArrayList không thể sử dụng được trong trường hợp cần phải đưa một nulldanh sách vào danh sách. Ngược lại, bạn có thể ngăn ArrayList chứa null bằng cách kiểm tra các giá trị trước khi thêm chúng hoặc sử dụng trình bao bọc ngăn chặn điều này xảy ra.

Có trường hợp nào tôi muốn thêm null vào ArrayList không?

Rõ ràng, bất kỳ trường hợp nào nullcó một ý nghĩa riêng biệt. Ví dụ, điều đó có nghĩa là giá trị tại một vị trí nhất định trong danh sách chưa được khởi tạo hoặc cung cấp.

Sẽ dễ dàng hơn nếu ArrayList đã ném một ngoại lệ trong mã nơi các phần tử đang được thêm vào.

Bạn có thể dễ dàng thực hiện hành vi đó bằng cách tạo một lớp bao bọc. Tuy nhiên, đây không phải là hành vi mà hầu hết các lập trình viên / ứng dụng cần.


8

Có trường hợp nào tôi muốn thêm null vào ArrayList không?

Chắc chắn, những gì về phân bổ trước? Bạn muốn một ArrayListtrong những thứ bạn chưa thể tạo vì bạn không có đủ thông tin. Chỉ vì bạn không thể nghĩ ra lý do tại sao một người nào đó có thể muốn làm điều gì đó không biến nó thành một ý tưởng tồi. Bất cứ điều gì. (Bây giờ, tôi chắc chắn rằng ai đó sẽ đi cùng và nói thay vào đó bạn nên có các đối tượng rỗng mà thực hiện một số mô hình họ đọc về trên một số diễn đàn tối nghĩa và các lập trình viên đó thực sự sẽ có thể chương trình ghi mà không bao giờ sử dụng ifbáo cáo, blah blah.)

Nếu các bạn có một hợp đồng không bao giờ nên có nulltrong một container thì tùy thuộc vào bạn để đảm bảo rằng hợp đồng đó được giữ nguyên, có lẽ là thích hợp nhất bằng cách khẳng định. Nó có thể đã đưa bạn tối đa 10 dòng mã. Java làm cho nó cực kỳ dễ dàng để bạn làm điều này. JDK không thể đọc được suy nghĩ của bạn.


1
Đối số preallocation của bạn không hợp lệ, vì điều này đã được xây dựng trong ArrayList với ensureCapacity(int minCapacity)phương thức và hàm ArrayList(int initialCapacity)tạo.
Philipp

7
@Philipp Giá trị nào sẽ đưa vào không gian đó?
James

Sự lựa chọn rõ ràng là đặt một nulltrong các mục được phân bổ trước (nhưng chưa được sử dụng ) của ArrayList; nhưng chúng ta có thể ensureCapacitylàm điều đó và không cho phép các chức năng khác như setlàm điều đó. Những lý do được đưa ra ở nơi khác dường như mạnh mẽ hơn.
David K

@DavidK: Thật ý nghĩa khi nói rằng nó có thể setlà một mặt hàng cho bất kỳ giá trị nào có thể được trả lại get. Thậm chí nếu một nỗ lực bình thường để getmột mục mà không gian đã được giao nhưng không bao giờ được viết nên ném một ngoại lệ chứ không phải trở về null, nó vẫn sẽ là hữu ích để có một cặp phương pháp mà sẽ cho phép list1.setOrEraseIfNull(index, list2.getOrReturnNull(index))chứ không phải là đòi hỏiif (list2.valueSet(index)) list1.set(index, list2.get(index)); else list1.unset(index);
supercat

1
@DavidK: Cố gắng đọc một mục đã được phân bổ nhưng chưa được viết phải mang lại giá trị null hoặc ném ngoại lệ; nó dễ dàng hơn để trả về null.
supercat

4

Đây dường như là một câu hỏi triết học (phần mềm) ở đây.

ArrayList như một lớp tiện ích được thiết kế để hữu ích trong bối cảnh rộng của các trường hợp sử dụng có thể.

Trái với tuyên bố ẩn của bạn rằng việc chấp nhận null là một giá trị hợp lệ nên được khuyến khích, có nhiều ví dụ trong đó giá trị null là hoàn toàn hợp pháp.

Lý do quan trọng nhất nulldo not knowtương đương với bất kỳ loại tham chiếu nào, bao gồm các loại khung. Điều này ngụ ý rằng null không thể được thay thế trong mọi trường hợp bằng một phiên bản nothinggiá trị trôi chảy hơn .

Vì vậy, giả sử bạn lưu trữ các số nguyên 1, 3, 7 trong danh sách mảng của mình theo chỉ mục tương ứng của nó: Do một số tính toán, bạn muốn lấy phần tử ở chỉ số 5, vì vậy mảng của bạn sẽ trả về là: "không có giá trị được lưu trữ". Điều này có thể đạt được thông qua việc trả về null hoặc NullObject . Trong hầu hết các trường hợp, trả về giá trị null được xây dựng là đủ biểu cảm. Sau khi gọi một phương thức có thể trả về null và sử dụng giá trị trả về của nó, việc kiểm tra giá trị được trả về so với null là khá phổ biến trong mã hiện đại.


4

Tuyên bố

File f = null;

là hợp lệ và hữu ích (tôi không nghĩ rằng tôi cần phải giải thích tại sao). Nó rất hữu ích để có một bộ sưu tập các tệp, một số trong đó có thể là null.

List<File> files = new ArrayList<File>();
// use my collection of files
// ....
// not using this one anymore:
files.set(3, null);

Tính hữu ích của các bộ sưu tập có thể chứa các đối tượng null tiến hành trực tiếp từ tính hữu ích của các đối tượng có thể là null. Nó thực sự đơn giản như vậy.


2

Dễ dàng chấp nhận mọi thứ hơn là bị hạn chế và sau đó thử mở thiết kế của bạn sau khi thực tế.

Ví dụ: điều gì sẽ xảy ra nếu họ tiên tri / mặt trời chỉ cung cấp NonNullableArrayList, nhưng bạn muốn có thể thêm Null vào danh sách của mình. Bạn sẽ làm điều này như thế nào? Bạn có thể sẽ phải tạo một đối tượng hoàn toàn khác, bạn không thể sử dụng mở rộng NonNullableArrayList. Thay vào đó, nếu bạn có một ArrayList lấy mọi thứ, bạn có thể dễ dàng mở rộng nó và ghi đè thêm, nơi nó không chấp nhận giá trị null.


2
Không đồng ý. Cho phép quá nhiều khó khăn hơn để sửa chữa một khi việc sử dụng sai đã thấy sử dụng rộng rãi. Nếu các nhà thiết kế ngôn ngữ cho phép null ở giai đoạn sau, nó sẽ không phá vỡ các chương trình hiện có, nhưng điều ngược lại là không đúng.
Andres F.

2
Nhưng tôi đồng ý. Đó là về tối đa hóa chức năng. Bạn có thể sử dụng một danh sách chấp nhận null ngay cả khi bạn không cần chúng. Bạn không thể sử dụng danh sách từ chối null nếu bạn cần null.
Florian F

-1

Tính hữu dụng của null?

Rất nhiều khi bạn đi với một Danh sách Danh sách để mô hình một tấm đời thực 2D với các vị trí khác nhau. Một nửa trong số các vị trí đó có thể trống (theo tọa độ ngẫu nhiên) trong khi các vị trí được điền không đại diện cho một int hoặc String đơn giản nhưng được đại diện bởi một đối tượng phức tạp. Nếu bạn muốn giữ thông tin vị trí, bạn cần điền vào chỗ trống bằng null để danh sách của bạn.get (x) .get (y)không dẫn đến những bất ngờ khó chịu. Để chống lại các ngoại lệ null, bạn có thể kiểm tra null (rất phổ biến) hoặc bạn có thể sử dụng Tùy chọn (mà tôi thấy tiện dụng trong trường hợp này). Giải pháp thay thế sẽ là lấp đầy các điểm trống bằng các vật thể "rác" có thể dẫn đến tất cả các loại lộn xộn trên đường. Nếu kiểm tra null của bạn thất bại hoặc bị lãng quên ở đâu đó, Java sẽ cho bạn biết. Mặt khác, một đối tượng giữ chỗ "rác" không được kiểm tra đúng cách có thể vượt qua không được chú ý.

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.