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

时间:2017-07-20 09:16:03

标签: java java-9

使用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)会发生同样的事情。

8 个答案:

答案 0 :(得分:62)

正如其他人指出的那样,the Map contract允许拒绝空值......

  

[S] ome实现禁止null键和值[...]。尝试插入不合格的密钥或值会引发未经检查的异常,通常为NullPointerExceptionClassCastException

...和收集工厂(不只是在地图上)make use of that

  

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

但为什么?

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

你可以听Stuart Marks解释它in this talk,但是JEP 269(引入工厂方法的那个)也总结了它:

  

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

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

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

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

答案 1 :(得分:8)

确切地说 - 允许HashMap存储null,静态工厂方法返回的Map所有地图都不相同。

一般来说,据我所知,首先允许HashMap中的空值作为键是错误的,新的集合禁止这种可能性开始。

如果您的HashMap中的条目具有某个键且值== null,请考虑这种情况。你得到它,它返回null。什么意思?它的映射为null或者它不存在?

同样适用于Key - 来自此类null键的哈希码必须始终特别处理。禁止空值开头 - 让这更容易。

答案 2 :(得分:8)

虽然HashMap确实允许空值,但Map.of不使用HashMap并且如果将 openAddCustomerDialog() { let dialogRef = this.dialog.open(AddCustomerComponent, { height: '600px', width: '600px', hasBackdrop:false } ); dialogRef.afterClosed().subscribe(result => { console.log("selected in msgbox" + result); }); } 用作键或值,则抛出异常:

  

Map.of()和Map.ofEntries()静态工厂方法提供了一种创建不可变映射的便捷方法。这些方法创建的Map实例具有以下特征:

     
      
  • ...

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

  •   

答案 3 :(得分:5)

主要区别在于:当您构建自己的Map“选项1”方式时......然后您隐式地说:“我希望拥有完整的自由在我正在做的事情中。“

因此,当您确定您的地图应具有空键或值时(可能出于here列出的原因),您可以自由地执行此操作。

但“选项2”是一个方便的东西 - 可能意图用于常量。 Java背后的人简单地决定:“当你使用这些便利方法时,生成的地图应该是无空的”。

允许空表示

 if (map.contains(key)) 

不同
 if (map.get(key) != null)

有时可能会出现问题。或者更确切地说:在处理那个地图对象时要记住这一点。

只是一个轶事暗示为什么这似乎是一种合理的方法:我们的团队自己实施了类似的便利方法。并猜测:在不了解未来Java标准方法的计划的情况下 - 我们的方法完全相同:它们返回传入数据的不可变副本;当你提供null元素时它们就会抛出。我们甚至非常严格,当您传入列表/地图/ ...时,我们也会抱怨。

答案 4 :(得分:5)

在地图中允许空值是一个错误。我们现在可以看到 ,但我想在引入HashMap时并不清楚。 NullpointerException是生产代码中的this question

我可能会说JDK会帮助开发人员对抗NPE瘟疫。一些例子:

  • Optional
  • 简介
  • Collectors.toMap(keyMapper, valueMapper)不允许keyMappervalueMapper函数返回null
  • 如果找到的值为Stream.findFirst() ,则
  • Stream.findAny()null会抛出NPE

因此,在新的JDK9不可变集合(和映射)中禁止null只是朝着同一个方向发展。我们都应该感谢它!

答案 5 :(得分:3)

并非所有地图都允许将null作为键

docs of Map中提到的原因。

  

某些地图实施对其可能包含的键和值有限制。例如,某些实现禁止空键和值,有些实现对其键的类型有限制。尝试插入不合格的键或值会引发未经检查的异常,通常是NullPointerException或ClassCastException。

答案 6 :(得分:2)

文档没有说为什么不允许 null

  

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

在我看来,Map.of()Map.ofEntries()静态工厂方法,它们将产生一个常量,主要由编译类型的开发人员构成。那么,保持null作为键或值的原因是什么?

然而,Map#put通常用于在运行时填充地图,其中可能会出现null个键/值。

答案 7 :(得分:0)

在我看来,不可为空对键有意义,但对值没有意义。

  1. Map界面变成了一个弱合同,如果不查看实现,就无法相信它的行为,那是假定您有权查看它。
  2. 对于键具有空值或根本不具有键的值不会,并且可以说不应被视为另一回事。
  3. 现有的库大量使用map作为将属性传递给它们的方法,这是可选的,这使得Map.of()不能用作此类结构的构造。
  4. Kotlin在编译时强制执行可空性,并允许具有空值的映射没有问题。
  5. 不允许使用空值的原因可能是由于实现细节和创建者的方便。