简体   繁体   English

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

[英]Why Hashtable does not allow null keys or values?

As specified in JDK documentation, Hashtable does not allow null keys or values.正如 JDK 文档中所指定的,Hashtable 不允许空键或值。 HashMap allows one null key and any number of null values. HashMap 允许一个空键和任意数量的空值。 Why is this?为什么是这样?

Hashtable is the older class, and its use is generally discouraged. Hashtable 是较老的类,通常不鼓励使用它。 Perhaps they saw the need for a null key, and more importantly - null values, and added it in the HashMap implementation.也许他们看到需要一个空键,更重要的是 - 空值,并将其添加到 HashMap 实现中。

HashMap is newer, and has more advanced capabilities, which are basically just an improvement on the Hashtable functionality. HashMap 比较新,有更高级的能力,基本上只是对Hashtable 功能的改进。 When HashMap was created, it was specifically designed to handle null values as keys and handles them as a special case.在创建 HashMap 时,它专门设计用于将空值作为键处理并将它们作为特殊情况处理。

Edit编辑

From Hashtable JavaDoc :Hashtable JavaDoc

To successfully store and retrieve objects from a Hashtable, the objects used as keys must implement the hashCode method and the equals method.要成功地从 Hashtable 存储和检索对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

Since null isn't an object, you can't call .equals() or .hashCode() on it, so the Hashtable can't compute a hash to use it as a key.由于null不是对象,因此不能对其调用.equals().hashCode() ,因此Hashtable无法计算散列以将其用作键。

The main reason why Hashtable and ConcurrentHashMap do not allow null keys or values is because of the expectation that they are going to be used in a multi-threaded environment. Hashtable 和 ConcurrentHashMap 不允许空键或值的主要原因是因为期望它们将在多线程环境中使用。 For a minute, let's assume that null values are allowed.一分钟,让我们假设允许空值。 In this case the hashtable's "get" method has ambiguous behavior.在这种情况下,哈希表的“get”方法具有不明确的行为。 It can return null if the key is not found in the map or it can return null if the key is found and its value is null.如果在映射中找不到键,它可以返回 null,如果找到键并且它的值为 null,它可以返回 null。 When a code expects null values, it usually checks if the key is present in the map so that it can know whether the key is not present or the key is present but value is null.当代码需要空值时,它通常会检查映射中是否存在键,以便知道键是不存在还是键存在但值为空。 Now this code breaks in a multi-threaded environment.现在这段代码在多线程环境中中断了。 Let's take a look at below code:让我们看看下面的代码:

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

In the above code, let's say thread t1 calls the contains method and finds the key and it assumes that key is present and is ready for returning the value whether it is null or not.在上面的代码中,假设线程 t1 调用 contains 方法并找到键,它假定键存在并准备好返回值,无论它是否为空。 Now before it calls map.get, another thread t2 removes that key from the map.现在,在它调用 map.get 之前,另一个线程 t2 从地图中删除该键。 Now t1 resumes and returns null.现在 t1 恢复并返回 null。 However as per the code, the correct answer for t1 is KeyNotFoundException because the key has been removed.但是,根据代码,t1 的正确答案是 KeyNotFoundException,因为密钥已被删除。 But still it returns the null and thus the expected behavior is broken.但它仍然返回空值,因此预期的行为被破坏了。

Now, for a regular HashMap, it is assumed, that it is going to get called by a single thread, hence there is no possibility of key getting removed in the middle of "contains" check and "get".现在,对于常规 HashMap,假设它将被单个线程调用,因此不可能在“包含”检查和“获取”中间删除键。 So HashMap can tolerate null values.所以 HashMap 可以容忍空值。 However for Hashtable and ConcurrentHashMap, the expectations are clear that multiple threads are going to act on the data.然而,对于 Hashtable 和 ConcurrentHashMap,期望很明确,即多个线程将对数据进行操作。 Hence they cannot afford to allow null values and give out incorrect answer.因此,他们不能允许空值并给出不正确的答案。 Same logic goes for keys.同样的逻辑也适用于键。 Now the counter argument can be - the contains and get steps could fail for non null values for Hashtables and ConcurrentHashMaps, because another thread can modify the map/table before the second step gets executed.现在 counter 参数可以是 - 对于 Hashtables 和 ConcurrentHashMaps 的非空值,包含和获取步骤可能会失败,因为另一个线程可以在执行第二步之前修改映射/表。 That is correct, it can happen.这是正确的,它可能发生。 But since Hashtables and ConcurrentHashMaps do not allow null keys and values, it is not necessary for them to implement contains and get check in the first place.但是由于 Hashtables 和 ConcurrentHashMaps 不允许空键和值,因此它们没有必要首先实现 contains 和 get check。 They can directly get the value because they know that if the get method returns null, the only reason for that is the key is not present and not because the value could be null.他们可以直接获取值,因为他们知道如果 get 方法返回 null,唯一的原因是键不存在,而不是因为值可能为 null。 The contains and get check is necessary only for HashMaps because they allow the null values and thus need to resolve the ambiguity about whether the key is not found or the value is null. contains 和 get 检查只对 HashMap 是必要的,因为它们允许空值,因此需要解决关于是否找不到键或值为空的歧义。

The reason is the reason on the accepted answer: Hashtable is old.原因是接受的答案的原因:Hashtable 很旧。

However, the use of Hashtable IS NOT discouraged in favor of HashMap in every scenario.但是,不鼓励在每种情况下使用 Hashtable 来支持 HashMap。

  • Hashtable is synchronized, so it is THREAD-SAFE . Hashtable 是同步的,所以它是THREAD-SAFE HashMap is not. HashMap 不是。

Neither Hashtable nor ConcurrentHashMap support null keys or values. Hashtable 和 ConcurrentHashMap 都不支持 null 键或值。 HashMap does. HashMap 确实如此。

If you want a drop-in replacement that doesn't require anything else than changing the class and works in every scenario, there is none.如果您想要一个插入式替换,除了更改类之外不需要任何其他内容并且在每种情况下都可以使用,那么没有。 The most similar option would be ConcurrentHashMap (which is thread safe but doesn't support locking the whole table):最相似的选项是ConcurrentHashMap (它是线程安全的,但不支持锁定整个表):

This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.在依赖其线程安全但不依赖于其同步细节的程序中,此类与 Hashtable 完全可互操作。

HashMap is a better replacement for single threaded applications or any time synchronization is not a requirement, because of the performance impact synchronization introduces. HashMap 是单线程应用程序的更好替代品,或者不需要任何时间同步,因为同步引入了性能影响。

Sources:资料来源:

Default Hashtable implementation has null check which thorws null pointer exception.默认 Hashtable 实现具有空检查,这会引发空指针异常。 Later on java developers might have realized the importance of null keys(for some default value etc) and values and that why HashMap got introduced.后来 Java 开发人员可能已经意识到空键(对于某些默认值等)和值的重要性,这也是 HashMap 被引入的原因。

For HashMap, null check is there for keys if the key is null then that element will be stored in a location where hashcode is not required.对于 HashMap,如果键为空,则对键进行空检查,则该元素将存储在不需要哈希码的位置。

so to conclude所以总结

Because in HashTable when you put an element it will take into account key and value hash.因为在 HashTable 中,当您放置元素时,它会考虑键和值哈希。 Basically you will have something like :基本上你会有类似的东西:

public Object put(Object key, Object value){

    key.hashCode();

    //some code

    value.hashCode();

}

HashTable - Does not allow null keys This is because, in put(K key, V value) method, we have key.hashcode() which throws null pointer exception. HashTable - 不允许空键 这是因为,在 put(K key, V value) 方法中,我们有 key.hashcode() 会抛出空指针异常。 HashTable - Does not allow null value This is because, in put(K key, V value) method we have if(value==null){throw new NullPointerException HashTable - 不允许空值这是因为,在 put(K key, V value) 方法中,我们有 if(value==null){throw new NullPointerException

HashMap allows null values as it doesn't have any checks like HashTable, while it allows only one null key. HashMap 允许空值,因为它没有像 HashTable 这样的任何检查,而它只允许一个空键。 This is done with the help of putForNullKey method, which add the value to the 0th index of the internal Array every time the key is provided as null这是在 putForNullKey 方法的帮助下完成的,该方法在每次将键提供为 null 时将值添加到内部数组的第 0 个索引

Hash table is very old class , from JDK 1.0哈希表是非常古老的类,来自 JDK 1.0

To understand this, first of all we need to understand comments written on this class by author.要理解这一点,首先我们需要理解作者在这个类上写的评论。 “This class implements a hashtable, which maps keys to values. “这个类实现了一个哈希表,它将键映射到值。 Any non-null object can be used as a key or as a value.任何非空对象都可以用作键或值。 To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.”要成功地从哈希表中存储和检索对象,用作键的对象必须实现 hashCode 方法和 equals 方法。”

HashTable class is implemented on hashing mechanism, that's mean to store any key-value pair, its required hash code of key object. HashTable 类是在哈希机制上实现的,这意味着存储任何键值对,其所需的键对象哈希码。 If key would be null, it will not able to given hash ,it will through null pointer exception and similar case for value it is throwing null if the value is null.如果键为空,它将无法给出哈希值,它将通过空指针异常和类似的值情况,如果值为空,则抛出空值。

But later on it was realized that null key and value has its own importance that is why one null key and multiple null values are allowed in later implemented classes like HashMap class.但后来人们意识到空键和值有其自身的重要性,这就是为什么在后来实现的类(如 HashMap 类)中允许一个空键和多个空值的原因。

For hash map null keys will allow and there is a null check is there for keys if the key is null then that element will be stored in a zero location in Entry array.对于哈希映射,空键将允许,并且如果键为空,则对键进行空检查,则该元素将存储在 Entry 数组中的零位置。 null key we can use for some default value..我们可以将空键用于某些默认值..

=> Hashtable methods are synchronised it neveruses object based locking. => Hashtable 方法是同步的,它从不使用基于对象的锁定。

HashMap implements by considering it special HashMap 的实现是因为它的特殊性

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

Java 8 you cannot infer types for hash table. Java 8 你不能推断哈希表的类型。

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

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

Just as @Jainendra said, HashTable doesn't allow null key for call key.hashCode() in put() .正如@Jainendra 所说,HashTable 不允许在put()调用 key.hashCode() 为空键。

But it seems that no one clearly answer why null value isn't allowed.但似乎没有人明确回答为什么不允许空值。

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

The null check in put doesn't explain why null value is illegal, it just ensure non-null invariant. put 中的空检查并没有解释为什么空值是非法的,它只是确保非空不变。

The concrete answer for not allow null value is HashTable will call value.equals when call contains/remove .不允许空值的具体答案是 HashTable 在调用contains/remove时会调用value.equals

Hashtable does not allow null keys but HashMap allows one null key and any number of null values. Hashtable 不允许空键,但 HashMap 允许一个空键和任意数量的空值。 There is a simple explanation behind that.这背后有一个简单的解释。

put() method in hashmap does not call hashcode() when null is passed as key and null Key is handled as a special case.当 null作为 key传递并且 null Key 作为特殊情况处理,hashmap中的put()方法不会调用 hashcode() HashMap puts null key in bucket 0 and maps null as key to passed value. HashMap 将空键放在桶 0 中并将空作为键映射到传递的值。

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

As seen in the algorithm the put() method checks if the key is null and call putForNullKey(value) and return.如算法中所见, put()方法检查键是否为空并调用 putForNullKey(value) 并返回。 This putForNullKey will create a entry in bucket at 0 index.这个 putForNullKey 将在桶中的 0 索引处创建一个条目。 Index zero is always reserved for null key in the bucket.索引零始终为存储桶中的空键保留。

On the other hand, in case of hashtable objects used as keys must implement the hashCode method and the equals method.另一方面,如果哈希表对象用作键,则必须实现 hashCode 方法和 equals方法。 Since null is not an object, it can't implement these methods.由于 null 不是对象,因此无法实现这些方法。

Hashtable is a class which came with the first version of java. Hashtable 是 Java 的第一个版本附带的一个类。 When it was released Java engineers tried to discourage the use of null keys or maybe did not realize its usefulness.当它发布时,Java 工程师试图阻止使用空键,或者可能没有意识到它的用处。 So, they did not allow it in the Hashtable.因此,他们不允许在 Hashtable 中使用它。

The put method to insert key value pair in Hashtable throws NullPointerException if value is null.如果 value 为 null,则在Hashtable 中插入键值对的 put 方法抛出 NullPointerException Since Hashtable is based on hashing mechanism, hash is calculated for keys, which throws NullPointerException if key is null.由于Hashtable是基于hash机制的,所以对key进行hash计算,如果key为null则抛出NullPointerException。

Later Java engineers must have realized that having a null key and values has its uses like using them for default cases.后来的 Java 工程师一定已经意识到,拥有一个空键和值有其用途,就像将它们用于默认情况一样。 So, they provided HashMap class with collection framework in Java 5 with capability of storing null key and values.因此,他们在 Java 5 中为 HashMap 类提供了具有存储空键和值的能力的集合框架。

The put method to insert key value pair in HashMap checks for null key and stores it at the first location of the internal table array.在 HashMap 中插入键值对的 put 方法检查空键并将其存储在内部表数组的第一个位置。 It isn't afraid of the null values and does not throw NullPointerException like Hashtable does.它不怕空值,也不会像 Hashtable 那样抛出 NullPointerException。

Now, there can be only one null key as keys have to be unique although we can have multiple null values associated with different keys.现在,只能有一个空键,因为键必须是唯一的,尽管我们可以有多个与不同键相关联的空值。

HashTable - Does not allow null keys HashTable - 不允许空键
This is because, in put(K key, V value) method, we have key.hashcode() which throws null pointer exception.这是因为,在 put(K key, V value) 方法中,我们有key.hashcode()会抛出空指针异常。
HashTable - Does not allow null value HashTable - 不允许空值
This is because, in put(K key, V value) method we have if(value==null){throw new NullPointerException这是因为,在 put(K key, V value) 方法中,我们有if(value==null){throw new NullPointerException

HashMap allows null values as it doesn't have any checks like HashTable, while it allows only one null key. HashMap 允许空值,因为它没有像 HashTable 这样的任何检查,而它只允许一个空键。 This is done with the help of putForNullKey method, which add the value to the 0th index of the internal Array every time the key is provided as null这是在 putForNullKey 方法的帮助下完成的,该方法在每次将键提供为 null 时将值添加到内部数组的第 0 个索引

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM