为什么Hashtable不允许null键或值?

44

根据JDK文档,Hashtable不允许使用null键或值。HashMap允许一个null键和任意数量的null值。为什么会这样呢?


由于在单个映射中键不能重复,所以出现了这种情况。 - Dangling Piyush
2
也许这个答案能帮到你。 - Vic
8
Hashtable通过这样做来执行一个协议。该协议确保应用于Hashtable的get(.)方法仅在键不在映射中时返回null。 - Alexandre Dupriez
可能是为什么Hashtable不接受null键?的重复问题。 - Krease
10个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
56

Hashtable是旧的类,通常不建议使用。也许他们看到了需要使用null键和更重要的是null值,并在HashMap实现中添加了它。

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

编辑

来自Hashtable JavaDoc:

  

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

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


27
ConcurrentHashMap是一个较新的类,但它也有不允许使用null键或值的限制。出于性能原因,他们添加了这个限制,因为支持null键和值需要额外的工作量,但大多数情况下并没有用处。 - DaoWen
4
我理解你希望知道为什么Hashtable不允许使用null作为键或值,尽管你提到了新的和旧的。 - Kumar Abhishek
我认为由于哈希表是同步的,它不能持有或锁定空对象。 - Khalid Nouh

20
Hashtable和ConcurrentHashMap不允许使用null键或值的主要原因是它们预期将在多线程环境中使用。如果允许使用null值,则Hashtable的“get”方法会产生模糊行为。如果在地图中未找到密钥,则可以返回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,它被认为只会被单个线程调用,因此不可能在“contains”检查和“get”之间删除键。因此HashMap可以容忍空值。然而,对于Hashtable和ConcurrentHashMap,期望很明确,多个线程将对数据进行操作。因此,它们不能允许空值并给出错误的答案。相同的逻辑也适用于键。现在可以反驳的是-对于HashTable和ConcurrentHashMap,包含和获取步骤可能会针对非空值失败,因为另一个线程可以在执行第二个步骤之前修改映射/表。这是正确的,可能会发生这种情况。但由于哈希表和ConcurrentHashMap不允许空键和值,所以它们不需要实现首先包含和获取检查。它们可以直接获取该值,因为它们知道如果get方法返回null,唯一的原因是该键不存在,而不是值为null。仅对于HashMap需要包含和获取检查,因为它们允许空值,因此需要解决关于未找到键还是值为null的歧义问题。

2
原因是接受答案中的原因:Hashtable已经过时了。 然而,在每种情况下,使用Hashtable并不被弃用以支持HashMap。 Hashtable是同步的,因此它是线程安全的。HashMap则不是。 Hashtable和ConcurrentHashMap都不支持null键或值。HashMap支持。 如果你想要一个不需要任何其他东西的替代品,只需更改类并在每种情况下都能正常工作,那么没有。最相似的选项将是ConcurrentHashMap(它是线程安全的,但不支持锁定整个表): 这个类在依赖于其线程安全性但不依赖于其同步细节的程序中,与Hashtable完全互操作。 对于单线程应用程序或任何时候同步不是必需的情况下,HashMap是更好的替代品,因为同步会带来性能影响。 来源:

ConcurrentHashMap put: 在此表中将指定的键映射到指定的值。键和值都不能为null。请检查您的答案。 - Carlos Verdes
糟糕!感谢您提醒我!我选择了显而易见的想法,认为ConcurrentHashMap和HashMap之间唯一的区别是线程安全性。这不是第一次发生了。多么尴尬的命名约定! - NotGaeL

2

默认的Hashtable实现有空指针检查,会抛出空指针异常。后来Java开发人员可能意识到了空键(例如某些默认值)和值的重要性,因此引入了HashMap。

对于HashMap,如果键为空,则会进行空检查,然后将该元素存储在不需要哈希码的位置上。


2

因为在哈希表中,当您放置元素时,它会考虑键和值的哈希。基本上你会得到像这样的东西:

```java HashTable.put(key, value); ```

所以总结一下:

public Object put(Object key, Object value){

    key.hashCode();

    //some code

    value.hashCode();

}

哈希表 - 不允许空键 这是因为在put(K key, V value)方法中,我们有key.hashcode()会抛出空指针异常。 哈希表 - 不允许空值 这是因为在put(K key, V value)方法中,如果value==null,就会抛出NullPointerException。

HashMap允许null值,因为它没有像HashTable那样的检查,但它只允许一个null键。这是通过putForNullKey方法实现的,每当提供null键时,它都会将值添加到内部数组的第0个索引中。


2
胡说八道。Hashtable从不调用value.hashCode() - rustyx

1

Hashtable是Java的第一个版本中提供的一个类。当它被发布时,Java工程师试图阻止使用null键或者可能没有意识到它的有用性。所以,在Hashtable中不允许使用它。

在Hashtable中插入键值对的put方法,如果值为null,则会抛出NullPointerException异常。由于Hashtable基于哈希机制,因此计算键的哈希值,如果键为null,则会抛出NullPointerException异常。

后来,Java工程师一定意识到了具有null键和值的用途,例如将其用作默认情况。因此,他们在Java 5中的集合框架中提供了HashMap类,具有存储null键和值的功能。

在HashMap中插入键值对的put方法检查null键并将其存储在内部表数组的第一个位置。它不会因为null值而感到害怕,并且不像Hashtable那样抛出NullPointerException异常。

现在,只能有一个null键,因为键必须是唯一的,虽然我们可以有多个与不同键相关联的null值。


0
正如@Jainendra所说,在put()中,HashTable不允许null键用于调用key.hashCode()。 但似乎没有人清楚地回答为什么不允许使用null值。
public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

在输入中进行的空值检查并没有解释为什么空值是非法的,它只是确保了非空不变量。

不允许空值的具体答案是,当调用contains/remove时,HashTable将调用value.equals


0

哈希表是一个非常古老的类,从JDK 1.0开始就存在。

要理解这个类,首先我们需要理解作者对这个类所写的注释。"这个类实现了一个哈希表,将键映射到值。任何非空对象都可以用作键或值。为了成功地从哈希表中存储和检索对象,用作键的对象必须实现hashCode方法和equals方法。"

HashTable类是基于哈希机制实现的,这意味着要存储任何键值对,需要使用键对象的哈希码。如果键为空,它将无法给出哈希码,会抛出空指针异常,对于值也是类似的情况,如果值为空,则会抛出null。

但后来人们意识到,空键和值具有自己的重要性,因此在后来实现的类(如HashMap类)中允许一个空键和多个空值。

对于哈希映射,空键将被允许,并且对于键进行了空检查,如果键为空,则该元素将存储在Entry数组的零位置。我们可以使用空键作为一些默认值。

=> Hashtable方法是同步的,它从不使用基于对象的锁定。

HashMap通过将其视为特殊实现。

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

在Java 8中,您无法推断哈希表的类型。

private Map<String,String> hashtable = new Hashtable<>(); // Not Allowed

private Map<String,String> hashtable = new HashMap<>(); // Allowed

0
Hashtable 不允许 null 键,但 HashMap 允许一个 null 键和任意数量的 null 值。这背后有一个简单的解释。 HashMap 中的 put() 方法在传递 null 作为键时不会调用 hashcode()方法,并将 null 键视为特殊情况处理。HashMap 将 null 键放在桶 0 中,并将 null 视为键映射到传递的值。
public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
 
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
如算法所示,put() 方法会检查键是否为 null 并调用 putForNullKey(value) 并返回。这个 putForNullKey 会在索引为0的bucket中创建一个条目。在bucket中,索引零始终保留给 null key。 另一方面,在哈希表中,作为键使用的对象必须实现 hashCode 方法和 equals 方法。由于 null 不是对象,因此它无法实现这些方法。

-3

哈希表 - 不允许空键
这是因为,在 put(K key, V value) 方法中,我们有 key.hashcode() 会抛出 null pointer exception。
哈希表 - 不允许空值
这是因为,在 put(K key, V value) 方法中,我们有 if(value==null){throw new NullPointerException

HashMap 允许 null 值,因为它没有像哈希表那样进行检查,同时仅允许一个 null 键。这是通过使用 putForNullKey 方法完成的,该方法每次提供 null 键时都将值添加到内部数组的第 0 个索引中。


类名是 java.util.Hashtable 而不是 "HashTable"! - Sujal Mandal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,