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

新手上路,请多包涵

在 Java 9 中,为 ListSetMap 接口引入了新的工厂方法。这些方法允许用一行中的值快速实例化一个 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) 的情况下。

原文由 hi.nitish 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
1 个回答

正如其他人指出 的那样, Map 合同 允许拒绝空值……

[S] 某些实现禁止 null 键和值 […]。尝试插入不合格的键或值会引发未经检查的异常,通常是 NullPointerExceptionClassCastException

… 并且收集工厂(不仅在地图上) 利用了它

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

但为什么?

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

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

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

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

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

所以不允许 null 有一些技术原因,但这样做也是为了使用创建的集合提高代码的稳健性。

原文由 Nicolai Parlog 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题