为什么 Map.of 不允许空键和值?但是为什么?

2022-08-31 09:31:28

在 Java 9 中,为 和 接口引入了新的工厂方法。这些方法允许在一行中快速实例化值的 Map 对象。现在,如果我们考虑:ListSetMap

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)
..

我无法理解,为什么在第二种情况下不允许空

我知道HashMap可以将null作为键和值,但是为什么在Map.of的情况下受到限制?

在 和 的情况下也会发生同样的情况。java.util.Set.of("v1", "v2", null)java.util.List.of("v1", "v2", null)


答案 1

正如其他人所指出的Map合约允许拒绝空值...

[S]ome 实现禁止键和值 [...]。尝试插入不符合条件的键或值会引发未经检查的异常,通常为 或 。nullNullPointerExceptionClassCastException

...收集工厂(不仅仅是在地图上)利用了这一点

它们不允许键和值。尝试使用键或值创建它们会导致 。nullnullNullPointerException

但是为什么?

允许集合现在被视为设计错误。这有多种原因。一个好的是可用性,其中最突出的麻烦制造者是 。如果它返回 ,则不清楚键是丢失还是值为 。一般来说,保证免费的集合更易于使用。在实现方面,它们还需要更少的特殊大小写,使代码更易于维护且性能更高。nullMap::getnullnullnull

你可以听斯图尔特·马克斯(Stuart Marks)在这次演讲中解释它,但JEP 269(介绍工厂方法的那个)也总结了它:

将不允许空元素、键和值。(最近引入的集合不支持 null。此外,禁止 nulls 提供了更紧凑的内部表示、更快的访问和更少的特殊情况的机会。

由于在慢慢发现它时已经处于野外,因此在不破坏现有代码的情况下更改它为时已晚,但是这些接口的大多数最新实现(例如ConcurrentHashMap)不再允许,工厂方法的新集合也不例外。HashMapnull

(我认为另一个原因是显式使用值被视为可能的实现错误,但我弄错了。这即将复制密钥,这也是非法的。null

因此,禁止有一些技术原因,但这样做也是为了使用创建的集合提高代码的健壮性。null


答案 2

恰好 - a 允许存储 null,而不是静态工厂方法返回的 null。并非所有地图都是相同的。HashMapMap

一般来说,据我所知,它首先有一个错误,允许在as键中使用null,较新的集合禁止这种可能性开始。HashMap

想想当你的条目中有一个条目具有某个键和值 == null 时的情况。你得到,它返回.这是什么意思?它具有映射或不存在?HashMapnullnull

来自此类空键的哈希码也必须始终得到特殊处理。从禁止空值开始 - 使这更容易。Key