Чому java.util.ArrayList дозволяє додати null?


41

Цікаво, чому java.util.ArrayListдозволяє додати null. Чи є випадок, коли я хотів би додати nullдо ArrayList?

Я задаю це питання, тому що в проекті у нас з'явилася помилка, де якийсь код додався nullдо, ArrayListі важко було помітити, де ця помилка. Очевидно, що NullPointerExceptionбуло кинуто, але не до того, як інший код не намагався отримати доступ до елемента. Проблема полягала в тому, як знайти код, який додав nullоб’єкт. Було б простіше, якби ArrayListкинути виняток у код, куди додавали елементи.


12
Проект Guava має досить цікаву сторінку на цю тему (вони не допускаються nullу більшості своїх колекцій).
Йоахім Зауер

6
Я вважаю, що наведені тут відповіді добре висвітлюють питання. Можливо, варто сказати одне: не сприймайте все, що є в JDK, як святе та ідеальне, а тоді беріть голову, намагаючись зрозуміти, чому це так "ідеально". Деякі речі - це (чесно, IMHO) помилки, які залишилися там завдяки зворотній сумісності, і це все. Навіть творці Java визнають це, просто прочитайте книги Джошуа Блоха, щоб побачити критику певних Java API. У будь-якому випадку, ваше питання зводиться до погоди, не існує більш елегантного способу зловити NPE на Java. Відповідь - ні, але повинно бути.
Shivan Dragon

2
Чи можете ви надати більше інформації про те, чому її не можна дозволити? Якщо це лише питання смаку, то слід віддати перевагу менш обмежувальним.
Mare Infinitus

Проста відповідь полягає лише в тому, що nullце за замовчуванням спосіб представлення відсутніх даних на Java, хотіли ви це чи ні. Насправді багатьом це не подобається, і це робить аргументом для функціональних речей у стилі програмування. Відповідь, яка найбільше обґрунтовується, не має сенсу / не відображає суті питання.
xji

@JIXiang Ви надто спрощуєте те, що nullє. "Відсутній" - лише одне з кількох потенційних тлумачень null. Інші допустимі тлумачення можуть бути "Невідомими", "Не застосовуються" або "Неініціалізованими". Що nullпредставляє, залежить від програми. Як сказало б спільнота Python, "За умови неоднозначності відмовтеся від спокуси здогадатися". Відмова утримувати null у контейнері, який цілком здатний це зробити, було б лише цим - здогадкою.
riwalk

Відповіді:


34

Таке дизайнерське рішення, як правило, визначається найменуванням.

Ім'я ArrayList пропонує читачеві функціонал, подібний до масивів - і дизайнерам Java Collections Framework цілком природно розраховувати, що переважна більшість користувачів API буде покладатися на його функціонування, подібне до масивів.

Зокрема, це стосується обробки нульових елементів. Користувач API знає, що нижче працює нормально:

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

був би дуже здивований, дізнавшись, чи схожий код для ArrayList кине NPE:

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

Таке обґрунтування наведено в підручнику JCF, що підкреслює точки, що передбачають близьку схожість між ArrayList та звичайними масивами:

ArrayList ... пропонує постійний доступ до позиції та працює просто швидко ...

Якщо ви хочете, щоб реалізація списку забороняла нулі, краще називатись так, NonNullableArrayListабо щось подібне, щоб не плутати користувачів API.


Бічна примітка в коментарях нижче є допоміжна дискусія, а також додаткові міркування, що підтверджують викладені тут міркування.


12
Це пояснення не є переконливим, враховуючи, що LinkedList також підтримує nullзаписи в списку.
Стівен C

12
Так ... але більш просте і (ІМО) більш правдоподібне пояснення полягає в тому, що дозволити nullзаписи корисно у багатьох випадках.
Stephen C

7
ArrayList не називається так, тому що він імітує масив. Він називається так, тому що це список, реалізований як масив. Так само, як TreeMap не веде себе як дерево.
Флоріан Ф

11
Ця відповідь, ІМО, явно неправильна. Нулі дозволені, тому що в Java для кращого або гіршого (IMO, гірше) null багато використовується для представлення неініціалізованих або відсутніх значень. Як він отримав найбільше голосів і чек "прийняти"? Серйозно, я справді втрачаю віру в StackOverflow в наші дні.
user949300

8
Ця відповідь очевидно неправильна. Це просто непідтримувані здогадки про допущення нулів у реалізації списку. Ось зведення новин з Java колекцій , що дозволяють / забороняючи анулює; зверніть увагу , як вона не має нічого спільного з схожістю з масивами.
Андрес Ф.

32

Null може бути дійсним значенням для елемента списку. Скажімо, ваш список містить елементи, які представляють деякі необов'язкові дані про список користувачів і зберігається в тому ж порядку, що і користувачі. Якщо додаткові дані заповнені, то ваш список буде містити додаткові дані, інакше слот, відповідний користувачеві, буде недійсним. (Я впевнений, що є кращі приклади, але ви розумієте)

якщо ви не хочете дозволити додавання нулів, ви можете обернути список масиву власною обгорткою, яка кидала, коли нуль був доданий.


2
Це здається поганим прикладом того, чому слід дозволити нулі - є кращі способи подання необов'язкових даних, ніж використання нульових значень у списку.
casablanca

@casablanca Так, цілком згоден, це не чудовий приклад.
Сем Холдер

Я не можу знайти вагому причину, щоб ArrayList містив нулі, крім поганих сюрпризів для наступного розробника, щоб "відкрити" це: /
AndreasScheinert

7
@casablanca Хоча я погоджуюся з вами, що нулі слід уникати, щоб представити необов'язкові дані на Java, для кращого або гіршого, тобто "традиційного".
user949300

2
Важливий випадок використання: Ви хочете передати параметри деякій динамічній функції як список. У вас є кілька функцій, кожна з яких має різний набір параметрів, тому вам потрібен масив динамічного розміру, і ви завжди можете передавати null як правильний аргумент функції!
Фалько

19

ArrayListдозволяє нульовий дизайн. Це навмисно. Від javadoc :

"[ArrayList - це] реалізація змінного масиву інтерфейсу List. Реалізує всі необов'язкові операції зі списком та дозволяє всі елементи, включаючи null ."

Відповідь на "чому" полягає в тому, що якби це не ArrayList, він не був би корисним у випадках, коли необхідно внести nullсписок у список. Навпаки, ви можете запобігти ArrayList містити нулі шляхом тестування значень перед їх додаванням або використання обгортки, яка запобігає цьому.

Чи є випадок, коли я хотів би додати null до ArrayList?

Очевидно, будь-який випадок, коли nullмає виразне значення. Наприклад, це може означати, що значення в заданій позиції у списку не було ініціалізовано чи надано.

Було б простіше, якби ArrayList кинув виняток у код, куди додавались елементи.

Ви можете легко реалізувати таку поведінку, створивши клас обгортки. Однак це не та поведінка, якої потребує більшість програмістів / додатків.


8

Чи є випадок, коли я хотів би додати null до ArrayList?

Зрозуміло, що з попереднім розподілом? Вам потрібно ArrayListречі, які ви ще не можете створити, оскільки у вас недостатньо інформації. Тільки тому, що ви не можете придумати причину, чому хтось може захотіти щось зробити, це не є поганою ідеєю. Що б там не було. (Зараз я впевнений, що хтось підійде і скаже, що натомість у вас повинні бути порожні об'єкти, які виконують деякий зразок, про який вони читають, в якомусь незрозумілому блозі, і що програмісти дійсно зможуть писати програми, не використовуючи жодного разу ifзаяви, бла-бла.)

Якщо у вас, хлопці, є контракт, який ніколи не повинен бути nullв контейнері, то вам належить багато переконатися в дотриманні цього контракту, можливо, найбільш доцільно, стверджуючи. Це, мабуть, забрало б вам максимум 10 рядків коду. Java робить вам подібні речі неймовірно простими. JDK не може прочитати вашу думку.


1
Ваш аргумент попереднього розміщення недійсний, тому що це вже вбудований в ArrayList з ensureCapacity(int minCapacity)методом та ArrayList(int initialCapacity)конструктором.
Філіпп

7
@Philipp Які значення він поставить у цей простір?
Джеймс

Очевидним вибором є введення nullв заздалегідь виділених (але ще невикористаних ) записів ArrayList; але ми можемо це дозволити ensureCapacity, але ще не дозволити іншим функціям, таким як setце робити. Причини, наведені в іншому місці, здаються сильнішими.
Девід К

@DavidK: Є сенс сказати, що setелемент повинен мати можливість будь-якого значення, яке може бути повернуто get. Навіть якщо звичайна спроба getелемента, для якого було виділено простір, але ніколи не було написано, повинно викидати виняток, а не повертатися null, все одно корисно мати пару методів, які дозволять, list1.setOrEraseIfNull(index, list2.getOrReturnNull(index))а не вимагаютьif (list2.valueSet(index)) list1.set(index, list2.get(index)); else list1.unset(index);
supercat

1
@DavidK: Спроба прочитати запис, який був призначений, але ще не записаний, повинен або дати null, або викинути виняток; простіше повернути його до нуля.
supercat

4

Це, здається, більше (програмне забезпечення) філософського питання.

ArrayList як клас корисності призначений для корисного використання в широкому контексті можливих випадків використання.

Всупереч вашому прихованому твердженню про те, що прийняття нуля як дійсного значення слід відмовити, існує багато прикладів, коли нульове значення цілком законно.

Найбільш важлива причина - nullце do not knowеквівалент будь-якого еталонного типу, включаючи типи фреймворків. Це означає, що null не може бути замінений ні в якому разі більш вільною версією nothingзначення.

Отже, скажімо, що ви зберігаєте Integers 1, 3, 7 у своєму масиві масиву за відповідним індексом: Завдяки деяким обчисленням ви хочете отримати елемент у індексі 5, тому те, що має повернути ваш масив, є: "значення не зберігається". Це може бути досягнуто шляхом повернення null або NullObject . У більшості випадків повернення вбудованого нульового значення є достатньо виразним. Після виклику методу, який може повернути null, та використовуючи його повернене значення, перевірка повернутого значення проти null досить поширена в сучасному коді.


4

Заява

File f = null;

є дійсним і корисним (я не думаю, що мені потрібно пояснювати, чому). Так само корисно мати колекцію файлів, деякі з яких можуть бути нульовими.

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

Корисність колекцій, які можуть містити нульові об'єкти, виходить безпосередньо з корисності об'єктів, які можуть бути нульовими. Це насправді так просто.


2

Простіше прийняти все, ніж бути обмежувальним, а потім спробувати відкрити свій дизайн після факту.

Наприклад, що робити, якщо вони oracle / sun надавали лише NonNullableArrayList, але ви хотіли додати Null до свого списку. Як би ти це зробив? Ймовірно, вам доведеться створити зовсім інший об'єкт, ви не можете використовувати розширення NonNullableArrayList. Замість цього, якщо у вас є ArrayList, який приймає все, ви можете легко розширити його та замінити додавання, де воно не приймає нульових значень.


2
Не погоджуються. Дозволити занадто багато важче виправити, коли нещасна ситуація побачила широке застосування. Якби мовні дизайнери допустили нулі на більш пізньому етапі, це не порушило б існуючі програми, але навпаки не вірно.
Андрес Ф.

2
Але я згоден Йдеться про максимізацію функціональності. Ви можете використовувати список, який приймає нулі, навіть якщо вони вам не потрібні. Ви не можете використовувати список, який відмовляється від нулів, якщо вам потрібні нулі.
Флоріан Ф

-1

Корисність нуля?

Дуже сильно, коли ви збираєтесь зі списком списку моделювати двовимірну табличку реального життя з різними позиціями. Половина цих позицій може бути порожньою (у випадкових координатах), тоді як заповнені не представляють собою простий int або String, а представлені складним об'єктом. Якщо ви хочете зберегти інформацію про позицію, вам потрібно заповнити порожні місця нулем, щоб ваш list.get (x) .get (y)не призводить до неприємних сюрпризів. Щоб протистояти нульовим виняткам, ви можете перевірити наявність null (дуже популярно) або ви можете скористатись необов’язковим (що я вважаю корисним у цьому випадку). Альтернативою було б заповнити порожні плями «мотлохами» предметами, що може призвести до всякого безладу по дорозі. Якщо ваша нульова перевірка не вдалася або десь забута, Java повідомить вам про це. З іншого боку, "непотрібний" об'єкт-заповнювач, який не перевіряється належним чином, може пройти непомітно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.