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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45210398/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 01:54:27  来源:igfitidea点击:

Why does Map.of not allow null keys and values?

javajava-9

提问by hi.nitish

With Java 9, new factory methods have been introduced for the List, Setand Mapinterfaces. These methods allow quickly instantiating a Map object with values in one line. Now, if we consider:

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

Map<Integer, String> map1 = new HashMap<Integer, String>(Map.of(1, "value1", 2, "value2", 3, "value3"));
map1.put(4, null);

The above is allowed without any exception while if we do:

以上是允许的,没有任何例外,而如果我们这样做:

Map<Integer, String> map2 = Map.of(1, "value1", 2, "value2", 3, "value3", 4, null );

It throws:

它抛出:

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:221)
..

I am not able to get, why null is not allowedin second case.

我无法得到,为什么在第二种情况下不允许 null

I know HashMap can take null as a key as well as a value but why was that restricted in the case of Map.of?

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

The same thing happens in the case of java.util.Set.of("v1", "v2", null)and java.util.List.of("v1", "v2", null).

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

回答by Suresh Atta

Not all Maps allow null as key

并非所有 Map 都允许 null 作为键

The reason mentioned in the docs of Map.

中提到的原因docs of Map

Some map implementations have restrictions on the keys and values they may contain. For example, some implementations prohibit null keys and values, and some have restrictions on the types of their keys. Attempting to insert an ineligible key or value throws an unchecked exception, typically NullPointerException or ClassCastException.

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

回答by Eugene

Exactly - a HashMapis allowed to store null, notthe Mapreturned by the static factory methods. Not allmaps are the same.

没错-一个HashMap允许存储空,没有Map由静态工厂方法返回。并非所有地图都相同。

Generally as far as I know it has a mistake in the first place to allow nulls in the HashMapas keys, newer collections ban that possibility to start with.

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

Think of the case when you have an Entry in your HashMapthat has a certain key and value == null. You do get, it returns null. What does the mean? It has a mapping of nullor it is not present?

想想当您的条目中HashMap有一个具有特定键和值 == 空的条目时的情况。你得到,它返回null。这是什么意思?它有映射null还是不存在?

Same goes for a Key- hashcode from such a null key has to treated specially all the time. Banning nulls to start with - make this easier.

这同样适用于一个Key-哈希码从这样的空键来专门处理所有的时间。禁止空值开始 - 让这更容易。

回答by 1615903

While HashMap does allow null values, Map.ofdoes not use a HashMapand throws an exception if one is used either as key or value, as documented:

虽然 HashMap 确实允许空值,Map.of但不使用 aHashMap并在将一个用作键或值时抛出异常,如文档所示

The Map.of() and Map.ofEntries() static factory methods provide a convenient way to create immutable maps. The Map instances created by these methods have the following characteristics:

  • ...

  • They disallow null keys and values. Attempts to create them with null keys or values result in NullPointerException.

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

  • ...

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

回答by Nicolai

As others pointed out, the Mapcontractallows rejecting nulls...

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

[S]ome implementations prohibit nullkeys and values [...]. Attempting to insert an ineligible key or value throws an unchecked exception, typically NullPointerExceptionor ClassCastException.

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

... and the collection factories (not just on maps) make use of that.

...和收集工厂(不仅仅是在地图上)利用了那个

They disallow nullkeys and values. Attempts to create them with nullkeys or values result in NullPointerException.

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

But why?

但为什么?

Allowing nullin collections is by now seen as a design error. This has a variety of reasons. A good one is usability, where the most prominent trouble maker is Map::get. If it returns null, it is unclear whether the key is missing or the value was null. Generally speaking, collections that are guaranteed nullfree are easier to use. Implementation-wise, they also require less special casing, making the code easier to maintain and more performant.

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

You can listen to Stuart Marks explain it in this talkbut JEP 269(the one that introduced the factory methods) summarizes it as well:

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

Null elements, keys, and values will be disallowed. (No recently introduced collections have supported nulls.) In addition, prohibiting nulls offers opportunities for a more compact internal representation, faster access, and fewer special cases.

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

Since HashMapwas already out in the wild when this was slowly discovered, it was too late to change it without breaking existing code but most recent implementations of those interfaces (e.g. ConcurrentHashMap) do not allow nullanymore and the new collections for the factory methods are no exception.

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

(I thought another reason was that explicitly using nullvalues was seen as a likely implementation error but I got that wrong. That was about to duplicate keys, which are illegal as well.)

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

So disallowing nullhad some technical reason but it was also done to improve the robustness of the code using the created collections.

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

回答by Andrew Tobilko

The documentation does not say whynullis not allowed:

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

They disallow nullkeys and values. Attempts to create them with nullkeys or values result in NullPointerException.

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

In my opinion, the Map.of()and Map.ofEntries()static factory methods, which are going to produce a constant, are mostly formed by a developer at the compile type. Then, what is the reason to keep a nullas the key or the value?

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

Whereas, the Map#putis usually used by filling a map at runtime where nullkeys/values could occur.

然而,Map#put通常通过在运行时填充null可能出现键/值的映射来使用。

回答by GhostCat

The major difference is: when you build your own Map the "option 1" way ... then you are implicitlysaying: "I want to have full freedomin what I am doing".

主要区别在于:当您以“选项 1”的方式构建自己的 Map 时……那么您就含蓄地说:“我希望在我正在做的事情上拥有完全的自由”。

So, when you decide that your map should have a null key or value (maybe for the reasons listed here) then you are free to do so.

因此,当您决定您的地图应该有一个空键或值(可能是出于此处列出的原因)时,您可以自由地这样做。

But "option 2" is about a convenience thing - probably intendedto be used for constants. And the people behind Java simply decided: "when you use these convenience methods, then the resulting map shall be null-free".

但是“选项 2”是关于方便的事情 - 可能旨在用于常量。Java 背后的人简单地决定:“当你使用这些方便的方法时,结果映射应该是空的”。

Allowing for null valuesmeans that

允许空意味着

 if (map.contains(key)) 

is not the same as

不一样

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

which might be a problem sometimes. Or more precisely: it is something to remember when dealing with that very map object.

有时这可能是一个问题。或者更准确地说:这是在处理那个地图对象时要记住的事情。

And just an anecdotal hint why this seems to be a reasonable approach: our team implemented similar convenience methods ourselves. And guess what: without knowing anything about plans about future Java standard methods - our methods do the exact same thing: they return an immutable copy of the incoming data; and they throw up when you provide null elements. We are even so strict that when you pass in emptylists/maps/... we complain as well.

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

回答by Federico Peralta Schaffner

Allowing nulls in maps has been an error. We can see it now, but I guess it wasn't clear when HashMapwas introduced. NullpointerExceptionis the most common bugseen in production code.

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

I would say that the JDK goes in the direction of helping developers fight the NPE plague. Some examples:

我会说 JDK 朝着帮助开发人员对抗 NPE 瘟疫的方向发展。一些例子:

  • Introduction of Optional
  • Collectors.toMap(keyMapper, valueMapper)doesn't allow neither the keyMappernor the valueMapperfunction to return a nullvalue
  • Stream.findFirst()and Stream.findAny()throw NPE if the value found is null
  • 介绍 Optional
  • Collectors.toMap(keyMapper, valueMapper)不允许函数keyMappervalueMapper函数返回null
  • Stream.findFirst()Stream.findAny()如果找到的值是,则抛出 NPEnull

So, I think that disallowing nullin the new JDK9 immutable collections (and maps) goes in the same direction.

所以,我认为null在新的 JDK9 不可变集合(和映射)中禁止是同一个方向。

回答by Pedro Borges

In my opinion non-nullability makes sense for keys, but not for values.

在我看来,不可空性对键有意义,但对值无效。

  1. The Mapinterface becomes a weak contract, you can't trust it's behaviour without looking at the implementation, and that is assuming you have access to see it. Java11's Map.of()does not allow null values while HashMapdoes, but they both implement the same Mapcontract - how come?
  2. Having a null value or no value at all could not, and arguably should not be considered as a distinct scenario. When you get the value for a key and you get null, who cares if it was explicitly put there or not? there's simply no value for the supplied key, the map does not contain such key, simple as that.
  3. Existing libraries make extensive use of map as a means to pass properties to them, many of them optional, this makes Map.of()useless as a construct for such structures.
  4. Kotlin enforces nullability at compile time and allows for maps with null values with no known issues.
  5. The reason behind not allowing null values is probably just due to implementation detail and convenience of the creator.
  1. Map接口变为弱合同,你不能没有看落实信任它的行为,这是假设你有机会看到它。Java11Map.of()不允许空值HashMap,但它们都实现了相同的Map契约——怎么会呢?
  2. 有一个空值或根本没有值是不可能的,并且可以说不应该被视为一个不同的场景。当你得到一个键的值并且你得到空值时,谁在乎它是否明确地放在那里?提供的键根本没有值,地图不包含这样的键,就这么简单。
  3. 现有的库广泛使用 map 作为向它们传递属性的一种手段,其中许多是可选的,这使得Map.of()作为此类结构的构造毫无用处。
  4. Kotlin 在编译时强制执行可空性,并允许使用没有已知问题的空值映射。
  5. 不允许空值背后的原因可能只是由于创建者的实现细节和便利性。