为什么Map.of不允许null键和值?
使用Java 9,已经为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)
的情况下java.util.Set.of("v1", "v2", null)
发生同样的情况。
正如其他人指出的, Map
合同允许零…
[…] ome实现禁止
null
键和值[…]。 尝试插入不合格的键或值将引发未检查的exception,通常为NullPointerException
或ClassCastException
。
…和收集工厂(不只是在地图上) 利用这一点 。
他们不允许
null
键和值。 尝试使用null
键或值创build它们会导致NullPointerException
。
但为什么?
在集合中允许null
现在被视为devise错误。 这有很多原因。 好的一个是可用性,其中最突出的麻烦制造者是Map::get
。 如果它返回null
,则不清楚该键是否丢失或值为null
。 一般来说,保证null
集合更易于使用。 实施方面,他们也需要较less的特殊shell,使代码更容易维护和更高性能。
你可以听斯图尔特·马克斯在这个讲座中解释它,但是JEP 269 (引入工厂方法的那个)也总结了这一点:
空的元素,键和值将被禁止。 (最近没有引入的集合支持空值)。另外,禁止空值为更紧凑的内部表示提供了机会,更快的访问和更less的特殊情况。
由于HashMap
在慢慢发现的时候就已经出现了,现在修改它已经太晚了,而不会破坏现有的代码,但是这些接口的最新实现(例如ConcurrentHashMap
)不再允许null
,并且工厂方法的新集合是没有例外。
(我还以为另一个原因是显式使用null
值被认为是一个可能的实现错误,但我得到了错误,那就是要复制密钥,这也是非法的。)
因此,禁止null
有一些技术上的原因,但是也使用创build的集合来提高代码的健壮性。
虽然HashMap确实允许空值,但Map.of
不会使用HashMap
并且如果使用key或value,则会引发exception,如文档所述 :
Map.of()和Map.ofEntries()静态工厂方法提供了创build不可变映射的便捷方式。 这些方法创build的Map实例具有以下特征:
…
他们不允许null键和值。 尝试使用空键或值创build它们会导致NullPointerException。
确切地说 – 一个HashMap
被允许存储null, 而不是静态工厂方法返回的Map
。 并不是所有的地图都是一样的。
一般来说,据我所知,首先允许HashMap
空值作为键是错误的,但新的集合禁止了这种可能性。
想象一下当你的HashMap
中有一个具有特定键和值== null的Entry的情况。 你确实得到了,它返回null
。 这是什么意思? 它有一个null
的映射或它不存在?
Key
– 从这样一个空密钥的哈希代码必须特别对待所有的时间。 禁用空位开始 – 使这更容易。
主要区别在于:当你build立你自己的地图的时候,你会隐含地说:“我想在我所做的事情上有充分的自由 ”。
所以,当你决定你的地图应该有一个空的键或值(可能是由于这里列出的原因),那么你可以自由地这样做。
但“选项2”是关于一个方便的东西 – 可能打算用于常量。 Java背后的人只是简单地决定:“当你使用这些方便的方法时,那么结果图就是无空的”。
允许空值意味着
if (map.contains(key))
是不一样的
if (map.get(key) != null)
这有时可能是一个问题。 或者更确切地说:当处理那个非常地图的对象的时候,这是要记住的。
只是一个轶事暗示为什么这似乎是一个合理的方法:我们的团队自己实施类似的便利方法。 猜测一下:如果不知道关于未来Java标准方法的计划,我们的方法会做同样的事情:它们会返回一个不可变的传入数据副本; 当你提供null元素时,他们会抛出。 我们甚至非常严格,以至于当你通过空的名单/地图/ …我们也抱怨。
并非所有地图都允许null作为关键字
docs of Map
提到的原因。
一些映射实现对它们可能包含的键和值有限制。 例如,一些实现禁止空键和值,一些实现对键的types有限制。 尝试插入不合格的键或值将引发未检查的exception,通常为NullPointerException或ClassCastException。
在地图中允许空值是一个错误。 现在我们可以看到它了 ,但是我猜不知道HashMap
何时被引入。 NullpointerException
是生产代码中最常见的错误 。
我可能会说JDK正在帮助开发者打击NPE瘟疫。 一些例子:
-
Optional
介绍 -
Collectors.toMap(keyMapper, valueMapper)
既不允许keyMapper
也不允许valueMapper
函数返回null
值 - 如果find的值为
null
则Stream.findFirst()
和Stream.findAny()
抛出NPE
所以,在新的JDK9不可变集合(和map)中不允许null
只是沿着相同的方向。 我们都应该感谢它!
文档没有说明为什么不允许null
:
他们不允许
null
键和值。 尝试使用null
键或值创build它们会导致NullPointerException
。
在我看来, Map.of()
和Map.ofEntries()
静态工厂方法将产生一个常量,大多由开发人员在编译types中形成。 那么,为什么要保留null
作为关键或价值呢?
然而, Map#put
通常是通过在运行时填充一个映射来使用的,其中null
键/值可能发生。