简体   繁体   English

为什么 java.util.Map.containsKey 会为不支持空键的映射抛出空指针异常?

[英]Why does java.util.Map.containsKey throw a null pointer exception for maps which don't support null keys?

The java docs say this: Java 文档是这样说的:

Throws:抛出:

ClassCastException - if the key is of an inappropriate type for this map (optional) ClassCastException - 如果该键的类型不适合此映射(可选)

NullPointerException - if the specified key is null and this map does not permit null keys (optional) NullPointerException - 如果指定的键为空并且此映射不允许空键(可选)

I am sure there is a good reason for this decision, but I don't know exactly what it is.我相信这个决定有一个很好的理由,但我不知道它到底是什么。 Shouldn't containsKey always return false even if the map doesn't allow null values as keys?即使映射不允许空值作为键,containsKey 也不应该总是返回 false 吗? Null clearly isn't a key in the map if the map doesn't allow keys, so I would think it should return false.如果地图不允许键,则 Null 显然不是地图中的键,所以我认为它应该返回 false。

The particular instance I am thinking of is a TreeMap.我想到的特定实例是 TreeMap。 I changed a Map instance from a HashMap to a TreeMap for performance reasons, and this caused a subtle bug, because containsKey for HashMap does not throw a null pointer exception.出于性能原因,我将 Map 实例从 HashMap 更改为 TreeMap,这导致了一个微妙的错误,因为 HashMap 的 containsKey 不会引发空指针异常。 I feel like there is not a good reason for this type of change to cause a subtle bug like this.我觉得这种类型的更改没有充分的理由导致像这样的微妙错误。


Edit I am aware of the (optional) nature of the NullPointerException.编辑我知道 NullPointerException 的(可选)性质。 Its optional nature does not, in my opinion, explain why it is allowed在我看来,它的可选性质并不能解释为什么允许它

To give a little more context as to why I am confused about this, containsKey is essentially comparing keys via equality.为了说明为什么我对此感到困惑,containsKey 本质上是通过相等来比较键。 I am, to some degree, coming from a functional background, wherein (in say, Haskell) the equivalent of containsKey would have an equality constraint on it, and would be valid for all values which can be compared via equality.在某种程度上,我来自函数式背景,其中(比如 Haskell)containsKey 的等价物将对其具有相等约束,并且对所有可以通过相等进行比较的值都有效。

The java docs for Object.equals says Object.equalsjava 文档

For any non-null reference value x, x.equals(null) should return false.对于任何非空引用值 x,x.equals(null) 应返回 false。

Therefore, I would think that containsKey must return false if the map does not support null keys, since the implementation "should" be equivalent to iterating over the keys with k.equals(input).因此,我认为如果映射不支持空键,则 containsKey 必须返回 false,因为实现“应该”等效于使用 k.equals(input) 迭代键。

I believe that this is a design decision that was made.我相信这是一个设计决定。

There are two possible scenarios:有两种可能的情况:

1. You use containsKey on a map that supports a null key. 1.您在支持空键的地图上使用 containsKey。 As the user of this function you expect to get either true or false.作为此函数的用户,您希望得到 true 或 false。 There's absolutely no reason to get back nullPointerException as we're seeing 'null' as any other key, so like you would expect 'someKey' to return false if it doesn't exist, same for null.绝对没有理由返回 nullPointerException,因为我们将 'null' 视为任何其他键,所以就像您期望 'someKey' 不存在时返回 false 一样,对于 null 也是如此。

2. You use containsKey on a map that doesn't support a null key. 2.您在不支持空键的地图上使用 containsKey。 Returning false would be a weird decision because it is not a possible key.返回 false 将是一个奇怪的决定,因为它不是一个可能的键。 Also the user might have a reference to some object which he tries to use as a key.此外,用户可能有对他试图用作键的某个对象的引用。 The user would prefer to get a NullPointerException so he would be aware of this situation, trying to get/set a null object as key.用户更愿意得到一个 NullPointerException 异常,所以他会意识到这种情况,试图获取/设置一个空对象作为键。

Your argument applies evenly to the ClassCastException : Shouldn't containsKey always return false even if the map doesn't allow that type of value as keys?您的论点平均适用于ClassCastException :即使映射不允许该类型的值作为键,也不应该containsKey总是返回 false 吗?

Answer: Instead of forcing implementations to check the given key for validity, the API (documented behavior) allows an exception to be thrown for invalid key values.回答:API(记录的行为)不是强制实现检查给定键的有效性,而是允许针对无效键值抛出异常。

Note that both exceptions are listed as "(optional)", which means that is it up to the implementation whether to do that.请注意,这两个例外都被列为“(可选)”,这意味着是否执行该操作取决于实现。 The implementation may choose to simply return false , or it may choose to throw one of those exceptions.实现可以选择简单地返回false ,或者它可以选择抛出这些异常之一。

Implementations will probably choose to not add special logic for this, so if the algorithm "natively" throws exception, then so be it.实现可能会选择为此添加特殊逻辑,因此如果算法“本机”抛出异常,那就这样吧。 The API was documented to allow this default behavior. API 被记录为允许这种默认行为。

Eg TreeMap will natively throw ClassCastException , because that will happen when trying to call compare() / compareTo() .例如, TreeMap将本机抛出ClassCastException ,因为在尝试调用compare() / compareTo()时会发生这种情况。 HashMap doesn't care about the object type, so will never throw ClassCastException . HashMap不关心对象类型,因此永远不会抛出ClassCastException

The logic of containsKey() for a map that doesn't support null keys may or may not do something that throws NullPointerException if the given key is null, and that is a valid results, so the logic doesn't have to waste extra code to handle it.对于不支持空键的映射, containsKey()的逻辑可能会或可能不会做一些事情,如果给定的键是空的,则抛出NullPointerException ,这是一个有效的结果,因此该逻辑不必浪费额外的代码来处理它。

First of all, containsKey(Object key) is declared in the Map<K, V> interface, which does not declare throwing anything , therefore, it depends on the implementation .首先,在Map<K, V>接口中声明了containsKey(Object key) ,它没有声明抛出任何东西,因此它取决于实现

If the question is why implementations do not do all those (null, cast) checks internally and why do they throw exceptions , I think it is because of design, and for a good reason, to clearly give the output what has gone wrong.如果问题是为什么实现不在内部执行所有这些 (null, cast) 检查以及为什么它们会抛出异常,我认为这是因为设计,并且有充分的理由,明确给出输出出了什么问题。

Note, that:注意:

  1. Your key, can be a subtype of a defined key type, therefore, it may need to be casted.您的密钥可以是已定义密钥类型的子类型,因此,可能需要对其进行强制转换。 As it is internally casted, runtime has no idea what you are going to provide as an argument.由于它是内部转换的,因此运行时不知道您将提供什么作为参数。 Hence, it's good to throw this;因此,最好抛出这个;
  2. Your key, might either be explicitly, or get somewhere, behind the scenes (in multithreaded scenario, for instance), get null , which is also the reason to process this scenario.您的密钥可能是明确的,也可能是在幕后的某个地方(例如,在多线程场景中), get null ,这也是处理此场景的原因。

Note, that both exceptions are defined as optional ones, so, again, it is NOT the must that implementation should be throwing them.请注意,这两个异常都被定义为可选异常,因此,再次强调,实现应该抛出它们并不是必须的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 java.util.HashMap.containsKey(Object key) 实现是否违反 java.util.Map.containsKey(Object key) 文档? - Does java.util.HashMap.containsKey(Object key) implementation violate java.util.Map.containsKey(Object key) documentation? 为什么这会引发Null Pointer Exception? - Why does this throw Null Pointer Exception? Java Lang Null指针异常,但是我不明白为什么? - Java Lang Null Pointer Exception, but I don't understand why? 为什么非空列表会引发空指针异常? - Why does a non-empty List throw a Null Pointer Exception? Map.ofEntries 在使用 containsKey() 检查 NULL 键时给出空指针异常 - Map.ofEntries gives Null Pointer Exception on checking NULL key using containsKey() 为什么ListView.setAdapter(null)不引发空指针异常? - Why doesn't ListView.setAdapter(null) throw a null pointer exception? Java空指针异常 - 不明白为什么 - Java null pointer exceptions - don't understand why 为什么Java突然在图像上返回空指针异常? - Why does Java return null pointer exception on image all of a sudden? FileParser Util Java方法显示空指针异常 - FileParser Util java method showing null pointer exception 为什么 Java 不告诉你哪个指针为空? - Why doesn't Java tell you which pointer is null?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM