Почему java.util.ArrayList позволяет добавлять нуль?

41

Интересно, почему java.util.ArrayListпозволяет добавить null. Есть ли какой-либо случай, когда я хотел бы добавить nullк ArrayList?

Я задаю этот вопрос , потому что в проекте мы имели ошибку , когда некоторый код добавлял nullк , ArrayListи это было трудно определить , где ошибка была. Очевидно, что NullPointerExceptionбыл брошен, но только когда другой код попытался получить доступ к элементу. Проблема заключалась в том, как найти код, который добавил nullобъект. Было бы проще, если ArrayListбы в коде было исключение, где добавлялись элементы.

Альфредо Осорио
источник
12
У проекта Guava есть довольно интересная страница на эту тему (они не допускаются nullв большинстве их коллекций).
Йоахим Зауэр
6
Я считаю, что ответы, приведенные здесь, хорошо охватывают вопрос. Может быть, стоит упомянуть одну вещь: не принимайте все, что есть в JDK, как святого и совершенного, а затем бейте головой, пытаясь понять, почему это так «идеально». Некоторые вещи (честно, ИМХО) ошибки, которые остались там из-за обратной совместимости, и все. Даже создатели Java признают это, просто прочитайте книги Джошуа Блоха, чтобы увидеть его критику определенных API Java. В любом случае, ваш вопрос сводится к погоде, нет более элегантного способа поймать NPE в Java. Ответ - нет, но так и должно быть.
Shivan Dragon
2
Можете ли вы предоставить больше информации о том, почему это не должно быть разрешено? Если это просто вопрос вкуса, то предпочтение следует отдавать менее ограничительным.
Mare Infinitus
Простой ответ - это просто nullспособ представления отсутствующих данных в Java по умолчанию, нравится вам это или нет. На самом деле многим это не нравится, и они делают это аргументом в пользу функционального стиля программирования. Ответ с наибольшим количеством голосов не имеет смысла / не отражает суть проблемы.
xji
@ JIXiang Ты слишком упрощаешь то, что nullесть. «Отсутствует» - это лишь одна из нескольких возможных интерпретаций null. Другими действительными интерпретациями могут быть «Неизвестно», «Не применимо» или «Неинициализировано». Что nullпредставляет, зависит от приложения. Как сказал бы сообщество Python: «Перед лицом двусмысленности откажитесь от соблазна гадать». Отказ удерживать значение NULL в контейнере, который вполне способен на это, был бы только предположением.
прогулка

Ответы:

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 ... предлагает постоянный доступ в режиме реального времени и просто быстро ...

Если вы хотите, чтобы реализация List не допускала пустые значения, ее лучше называть как- NonNullableArrayListто так или иначе, чтобы не запутывать пользователей API.


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

комар
источник
12
Это объяснение не убедительно, учитывая, что LinkedList также поддерживает nullзаписи в списке.
Стивен К,
12
Да ... но более простое и (IMO) более правдоподобное объяснение состоит в том, что разрешение nullзаписей полезно во многих случаях.
Стивен С
7
ArrayList не вызывается так, потому что он имитирует массив. Он называется так, потому что это список, реализованный в виде массива. Так же, как TreeMap не ведет себя как дерево.
Флориан Ф
11
Этот ответ, IMO, просто неверен. Нули допускаются, потому что в Java, к лучшему или к худшему (IMO, хуже), нуль часто используется для представления неинициализированных или пропущенных значений. Как он получил наибольшее количество голосов и чек "принять"? Серьезно, я действительно теряю веру в StackOverflow в эти дни.
user949300
8
Этот ответ совершенно неверный. Это просто неподдерживаемое предположение о том, что в реализации списка допускаются пустые значения. Вот обзор о коллекциях Java, разрешающих / запрещающих нули; обратите внимание, что это не имеет ничего общего с подобием массивов.
Андрес Ф.
32

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

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

Сэм Холдер
источник
2
Это кажется плохим примером того, почему пустые значения должны быть разрешены - есть лучшие способы представления необязательных данных, чем использование нулевых значений в списке.
Касабланка
@casablanca, да, полностью согласен, это не лучший пример.
Сэм Холдер
Я не могу найти вескую причину для ArrayList, содержащего нули, кроме неприятных сюрпризов для следующего разработчика, который «обнаружит» его: /
AndreasScheinert
7
@casablanca Хотя я согласен с вами, следует избегать использования нулей для представления необязательных данных, в Java, в лучшую или в худшую сторону, то есть «традиционных».
user949300
2
Надежный вариант использования: вы хотите передать параметры некоторой динамической функции в виде списка. У вас есть несколько функций, каждая с разным набором параметров, поэтому вам нужен динамически изменяемый массив, и вы всегда можете передать значение null в качестве допустимого аргумента функции!
Falco
19

ArrayListпозволяет нулевой дизайн. Это намеренно. От Javadoc :

«[ArrayList - это] реализация массива изменяемого размера интерфейса List. Реализует все необязательные операции со списками и разрешает все элементы, включая ноль ».

Ответ на вопрос «почему» заключается в том, что в противном случае ArrayList не будет использоваться в тех случаях, когда необходимо добавить его nullв список. Напротив, вы можете запретить ArrayList содержать нули, либо проверяя значения перед их добавлением, либо используя оболочку, которая предотвращает это.

Есть ли случай, когда я хотел бы добавить ноль в ArrayList?

Очевидно, любой случай, когда null имеет особое значение. Например, это может означать, что значение в данной позиции в списке не было инициализировано или предоставлено.

Было бы проще, если бы ArrayList выдал исключение в коде, где добавлялись элементы.

Вы можете легко реализовать это поведение, создав класс-оболочку. Однако такое поведение не требуется большинству программистов / приложений.

Стивен С
источник
8

Есть ли случай, когда я хотел бы добавить ноль в ArrayList?

Конечно, как насчет предварительного распределения? Вам нужны ArrayListвещи, которые вы еще не можете создать, потому что у вас недостаточно информации. Просто потому, что вы не можете придумать причину, по которой кто-то может захотеть что-то сделать, не делает это плохой идеей. Без разницы. (Теперь я уверен, что кто-то придет и скажет, что вместо этого у вас должны быть пустые объекты, которые соответствуют некоторому шаблону, о котором они читают в каком-то непонятном блоге, и что программисты действительно должны писать программы, даже не используяif утверждений, бла-бла.)

Если у вас, ребята, есть контракт, которого никогда не должно быть nullв контейнере, тогда вам решать , что контракт будет соблюден, вероятно, наиболее уместно, утверждая. Вероятно, это заняло бы максимум 10 строк кода. Java невероятно облегчает вам подобные вещи. JDK не может читать ваши мысли.

Джеймс
источник
1
Ваш аргумент preallocation недействителен, потому что он уже встроен в 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);
потребуют
1
@DavidK: Попытка прочитать запись, которая была выделена, но еще не записана, должна либо дать ноль, либо выдать исключение; проще сделать так, чтобы он возвращал ноль.
суперкат
4

Похоже, это скорее философский (программный) вопрос.

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

Вопреки вашему скрытому утверждению, что принятие значения NULL в качестве допустимого значения не рекомендуется, существует множество примеров, когда значение NULL является абсолютно допустимым.

Единственной наиболее важной причиной является то, что nullэто do not knowэквивалент любого ссылочного типа, включая типы каркаса. Это подразумевает, что нуль не может быть заменен в любом случае более беглой версией nothingзначения.

Итак, допустим, что вы храните целые числа 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 в свой список. Как бы вы это сделали? Вам, вероятно, придется создать совершенно другой объект, вы не можете использовать extension NonNullableArrayList. Вместо этого, если у вас есть ArrayList, который принимает все, вы можете легко расширить его и переопределить добавление, когда оно не принимает нулевые значения.

NiteRain
источник
2
Не согласившись. Разрешить слишком много труднее исправить после того, как неправильное использование получило широкое распространение. Если бы разработчики языка допустили пустые значения на более позднем этапе, это не сломало бы существующие программы, но обратное неверно.
Андрес Ф.
2
Но я согласен. Речь идет о максимизации функциональности. Вы можете использовать список, который принимает пустые значения, даже если они вам не нужны. Вы не можете использовать список, который отклоняет нули, если вам нужны нули.
Флориан F
-1

Полезность нуля?

Очень сильно, когда вы идете со списком списков, чтобы смоделировать двухмерную реальную пластину с разными позициями. Половина из этих позиций может быть пустой (в случайных координатах), в то время как заполненные позиции не представляют простое int или String, а представлены сложным объектом. Если вы хотите сохранить информацию о местоположении, вам нужно заполнить пустые места нулем, чтобы ваш list.get (x) .get (y)не приводит к неприятным сюрпризам. Чтобы противостоять нулевым исключениям, вы можете проверить на нулевое (очень популярное) или использовать опциональное (которое я считаю удобным в этом случае). Альтернативой было бы заполнить пустые места «мусорными» объектами, которые могут привести к всевозможным беспорядкам в будущем. Если ваша нулевая проверка не пройдена или где-то забыта, Java сообщит вам. С другой стороны, «ненужный» объект-заполнитель, который не проверен должным образом, может пройти незамеченным.

нанотелу
источник