Почему Map.of не разрешает нулевые ключи и значения?
В Java 9 были введены новые методы factory для интерфейсов List
, Set
и Map
. Эти методы позволяют быстро создать экземпляр объекта Map со значениями в одной строке. Теперь, если учесть:
Map<Integer, String> map1 = new HashMap<Integer, String>(Map.of(1, "value1", 2, "value2", 3, "value3"));
map1.put(4, null);
Вышеуказанное разрешено без каких-либо исключений, если мы делаем:
Map<Integer, String> map2 = Map.of(1, "value1", 2, "value2", 3, "value3", 4, null );
Он выбрасывает:
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:221)
..
Я не могу получить, , почему null не разрешен во втором случае.
Я знаю, что HashMap может принимать значение null как ключ, а также значение, но почему это было ограничено в случае Map.of?
То же самое происходит в случае java.util.Set.of("v1", "v2", null)
и java.util.List.of("v1", "v2", null)
.
Ответы
Ответ 1
Как отмечали другие, контракт Map
позволяет отклонять нули...
[S] ome реализации запрещают null
ключи и значения [...]. Попытка вставить несоответствующий ключ или значение вызывает исключение, исключая, обычно NullPointerException
или ClassCastException
.
... и сборщики (а не только на картах) используют это.
Они запрещают ключи и значения null
. Попытки создать их с помощью null
ключей или значений приведут к NullPointerException
.
Но почему?
Разрешение null
в коллекциях теперь рассматривается как ошибка проектирования. Это имеет множество причин. Хорошей является удобство использования, где наиболее заметным создателем проблем является Map::get
. Если он возвращает null
, неясно, отсутствует ли ключ или значение null
. Вообще говоря, коллекции, гарантированные null
бесплатно, проще в использовании. Реализация, они также требуют меньше специальных корпусов, что делает код более удобным для обслуживания и более эффективным.
Вы можете слушать Stuart Marks, объясните это в этом разговоре, но JEP 269 (тот, который ввел методы factory), также суммирует его:
Неверные элементы, ключи и значения будут запрещены. (Вновь созданные коллекции не поддерживали нули.) Кроме того, запрещение нулей предоставляет возможности для более компактного внутреннего представления, более быстрого доступа и меньшего количества особых случаев.
Так как HashMap
уже вышел в тупик, когда это было медленно обнаружено, было уже слишком поздно менять его, не нарушая существующий код, а самые последние реализации этих интерфейсов (например, ConcurrentHashMap
) больше не позволяют null
, и новые коллекции для методов factory не являются исключением.
(Я думал, что другая причина заключалась в том, что явно использование значений null
рассматривалось как вероятная ошибка реализации, но я ошибался. Это было похоже на дублирование ключей, которые также являются незаконными.)
Так что отказ от null
имел некоторые технические причины, но он также был сделан для повышения надежности кода с помощью созданных коллекций.
Ответ 2
Точно - a HashMap
разрешено сохранять значение null, а не Map
, возвращаемое статическими методами factory. Не все карты одинаковы.
В общем, насколько я знаю, во-первых, ошибка допускает нули в ключах HashMap
, но новые коллекции запрещают эту возможность.
Подумайте о случае, когда у вас есть запись в вашем HashMap
, которая имеет определенный ключ и value == null. Вы получаете, он возвращает null
. Что значит? Он имеет отображение null
или его нет?
То же самое относится к Key
- хэш-код из такого нулевого ключа должен обрабатываться специально все время. Запретить нули для начала - сделать это проще.
Ответ 3
В то время как HashMap разрешает нулевые значения, Map.of
не использует HashMap
и генерирует исключение, если оно используется как ключ или значение, как документированы:
Способы Map.of() и Map.ofEntries() static factory обеспечивают удобный способ создания неизменяемых карт. Экземпляры Map, созданные этими методами, имеют следующие характеристики:
Ответ 4
Основное различие заключается в следующем: когда вы создаете свою собственную карту "вариант 1"... тогда вы неявно говорите: "Я хочу иметь полную свободу в том, что я делаю".
Итак, когда вы решите, что ваша карта должна иметь нулевой ключ или значение (возможно, по причинам, указанным ниже здесь), тогда вы можете это сделать.
Но "вариант 2" - это удобная вещь - вероятно, предназначенная для использования в константах. И люди, стоящие за Java, просто решили: "когда вы используете эти удобные методы, тогда результирующая карта будет нулевой".
Разрешение нулевых значений означает, что
if (map.contains(key))
не совпадает с
if (map.get(key) != null)
что иногда может быть проблемой. Точнее: это то, что нужно помнить при работе с этим самым объектом карты.
И просто анекдотический намек на то, почему это кажется разумным подходом: наша команда сама применяла аналогичные методы удобства. И угадайте, что: ничего не зная о планах относительно будущих стандартных методов Java - наши методы делают то же самое: они возвращают неизменяемую копию входящих данных; и они вызывают, когда вы предоставляете нулевые элементы. Мы даже настолько строги, что, когда вы проходите в пустые списки/карты/... мы также жалуемся.
Ответ 5
Не все карты допускают null как клавишу
Причина, упомянутая в docs of Map
.
В некоторых реализациях карт есть ограничения на ключи и значения, которые они могут содержать. Например, некоторые реализации запрещают пустые ключи и значения, а некоторые имеют ограничения на типы их ключей. Попытка вставить недопустимый ключ или значение выдает исключение, исключающее исключение, обычно NullPointerException или ClassCastException.
Ответ 6
Разрешение нулей на картах было ошибкой. Мы можем видеть это сейчас, но, я думаю, было неясно, когда был введен HashMap
. NullpointerException
является наиболее распространенной ошибкой, видимой в производственном коде.
Я могу сказать, что JDK идет в сторону того, чтобы помочь разработчикам бороться с чумой NPE. Некоторые примеры:
- Введение
Optional
-
Collectors.toMap(keyMapper, valueMapper)
не позволяют ни функции keyMapper
, ни valueMapper
не возвращать значение null
-
Stream.findFirst()
и Stream.findAny()
выбросить NPE, если найденное значение null
Итак, запрет null
в новых неизменяемых коллекциях (и карте) JDK9 просто идет в одном направлении. И мы все должны благодарить его!
Ответ 7
В документации не указано, почему null
не разрешено:
Они запрещают клавиши null
и значения. Попытки создать их с помощью null
ключей или значений приведут к NullPointerException
.
На мой взгляд, методы Map.of()
и Map.ofEntries()
static factory, которые собираются создать константу, в основном формируются разработчиком при компиляции. Затем, в чем причина сохранения null
в качестве ключа или значения?
В то время как Map#put
обычно используется путем заполнения карты во время выполнения, где могут возникать ключи/значения null
.