为什么哈希表不允许空键或值?

2022-09-01 02:31:35

如 JDK 文档中所述,Hashtable 不允许空键或值。HashMap 允许一个空键和任意数量的空值。这是为什么呢?


答案 1

Hashtable是较旧的类,通常不鼓励使用它。也许他们看到了对空键的需求,更重要的是 - 空值,并将其添加到HashMap实现中。

HashMap更新,具有更高级的功能,这基本上只是对Hashtable功能的改进。创建HashMap时,它专门设计用于将空值作为键处理,并将它们作为特殊情况处理。

编辑

来自 JavaDocHashtable

要从 Hashtable 中成功存储和检索对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

由于 不是对象,因此您无法调用或在其上,因此无法计算哈希以将其用作键。null.equals().hashCode()Hashtable


答案 2

Hashtable 和 ConcurrentHashMap 不允许空键或值的主要原因是期望它们将在多线程环境中使用。一分钟,让我们假设允许空值。在这种情况下,哈希表的“get”方法具有不明确的行为。如果在映射中找不到键,它可以返回 null;如果找到键并且其值为 null,它可以返回 null。当代码需要 null 值时,它通常会检查映射中是否存在该键,以便它可以知道该键不存在,或者该键是否存在但值为 null。现在,此代码在多线程环境中中断。让我们看一下下面的代码:

if (map.contains(key)) {
    return map.get(key);
} else {
    throw new KeyNotFoundException;
}

在上面的代码中,假设线程 t1 调用 contains 方法并找到密钥,它假定该密钥存在并准备好返回该值,无论它是否为 null。现在,在调用 map.get 之前,另一个线程 t2 会从映射中删除该密钥。现在 t1 恢复并返回 null。但是,根据代码,t1 的正确答案是 KeyNotFoundException,因为该密钥已被删除。但它仍然返回 null,因此预期的行为被破坏。

现在,对于常规的HashMap,假设它将被单个线程调用,因此在“包含”检查和“get”中间没有密钥被删除的可能性。因此,HashMap可以容忍空值。然而,对于Hashtable和ConcurrentHashMap来说,人们的期望很明显,多个线程将对数据采取行动。因此,他们不能允许空值并给出不正确的答案。同样的逻辑也适用于键。现在,对于 Hashtables 和 ConcurrentHashMaps 的非空值,包含和获取步骤可能会失败,因为另一个线程可以在执行第二个步骤之前修改映射/表。这是正确的,它可能发生。但是由于 Hashtables 和 ConcurrentHashMaps 不允许空键和值,因此它们没有必要首先实现包含并获取检查。他们可以直接获取值,因为他们知道,如果 get 方法返回 null,则唯一的原因是键不存在,而不是因为该值可能为 null。包含和获取检查仅对 HashMaps 是必需的,因为它们允许空值,因此需要解决有关是找不到键还是值为空的歧义。