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

时间:2012-08-16 06:34:47

标签: java hashmap hashtable

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

9 个答案:

答案 0 :(得分:40)

Hashtable是较旧的类,通常不鼓励使用它。也许他们认为需要一个null键,更重要的是 - null值,并将它添加到HashMap实现中。

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

修改

来自Hashtable JavaDoc

  

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

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

答案 1 :(得分:2)

原因是接受答案的原因:Hashtable已经过时了。

但是,在每种情况下都不鼓励使用Hashtable支持HashMap。

  • Hashtable已同步,因此为THREAD-SAFE。 HashMap不是。

Hashtable和ConcurrentHashMap都不支持null键或值。 HashMap可以。

如果你想要一个不需要任何其他东西而不需要更改类并且在每个场景中工作的替代品,那么就没有了。最相似的选项是ConcurrentHashMap(这是线程安全的,但不支持锁定整个表):

  

此类可与依赖的程序中的Hashtable完全互操作   关于其线程安全性,但没有关于其同步细节。

HashMap是单线程应用程序的更好替代品,或者任何时间同步都不是必需的,因为同步引入了性能影响。

来源:

答案 2 :(得分:2)

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

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

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

现在,对于常规的HashMap,假定它将由单个线程调用,因此在“包含”检查和“获取”过程中不存在删除键的可能性。因此HashMap可以容忍null值。但是,对于Hashtable和ConcurrentHashMap,很明显期望多个线程将对数据起作用。因此,他们负担不起允许空值并给出错误的答案。密钥的逻辑相同。现在counter参数可以是-对于Hashtables和ConcurrentHashMaps的非null值,contains和get步骤可能会失败,因为另一个线程可以在执行第二步之前修改映射/表。没错,这有可能发生。但是,由于Hashtables和ConcurrentHashMaps不允许使用空键和空值,因此它们不必首先实现包含并获取检查。他们可以直接获取值,因为他们知道如果get方法返回null,则唯一的原因是不存在键,而不是因为值可能为null。包含和获取检查仅对于HashMaps才是必需的,因为它们允许使用null值,因此需要解决关于是否找到键或值是否为null的歧义。

答案 3 :(得分:1)

默认的Hashtable实现具有null检查,可以解决null指针异常。 后来在Java上,开发人员可能已经意识到空键(对于某些默认值等)和值的重要性,以及引入HashMap的原因。

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

答案 4 :(得分:0)

总结

因为在放置元素的HashTable中,它会考虑键和值哈希。基本上你会有类似的东西:

public Object put(Object key, Object value){

    key.hashCode();

    //some code

    value.hashCode();

}

HashTable - 不允许使用null键 这是因为,在put(K key,V value)方法中,我们有key.hashcode(),它抛出空指针异常。 HashTable - 不允许空值 这是因为,在put(K key,V value)方法中我们有if(value == null){throw new NullPointerException

HashMap允许空值,因为它没有像HashTable这样的任何检查,而它只允许一个空键。这是在putForNullKey方法的帮助下完成的,每次将密钥提供为null时,该方法都会将值添加到内部数组的第0个索引

答案 5 :(得分:0)

哈希表是一个非常古老的类,来自JDK 1.0

要理解这一点,首先我们需要理解作者在这个课上写的评论。 “这个类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或值。 要成功存储和检索哈希表中的对象,用作键的对象必须实现 hashCode方法和equals方法。“

HashTable类是在散列机制上实现的,这意味着存储任何键值对,它是键对象的必需哈希码。如果key为null,则无法给出hash,它将通过null指针异常和类似值的情况 如果值为null,则抛出null。

但后来人们意识到null键和值有其自身的重要性 为什么在后来实现的类(如HashMap类)中允许一个null键和多个null值。

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

=> 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

答案 6 :(得分:0)

就像@Jainendra所说的那样,HashTable不允许在put()中的调用key.hashCode()中使用空键。

但是似乎没有人明确回答为什么不允许使用空值。

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;
}

put的null检查并不能解释为什么null值是非法的,它只是确保非null不变。

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

答案 7 :(得分:0)

Hashtable不允许空键,但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的存储桶中创建一个条目。索引零始终为存储桶中的空键保留。

另一方面,对于哈希表,用作键的对象必须实现hashCode方法和equals 方法。由于null不是对象,因此无法实现这些方法。

答案 8 :(得分:-2)

HashTable - 不允许空键
这是因为,在put(K key,V value)方法中,我们有NaT抛出空指针异常。
HashTable - 不允许空值
这是因为,在put(K key,V value)方法中,我们有key.hashcode()

HashMap允许空值,因为它没有像HashTable这样的任何检查,而它只允许一个空键。这是在putForNullKey方法的帮助下完成的,每次将密钥提供为null时,该方法都会将值添加到内部数组的第0个索引