根据JDK文档,Hashtable不允许使用null键或值。HashMap允许一个null键和任意数量的null值。为什么会这样呢?
根据JDK文档,Hashtable不允许使用null键或值。HashMap允许一个null键和任意数量的null值。为什么会这样呢?
Hashtable是旧的类,通常不建议使用。也许他们看到了需要使用null键和更重要的是null值,并在HashMap实现中添加了它。
HashMap是较新的类,具有更高级的功能,基本上只是对Hashtable功能的改进。当创建HashMap时,它专门设计用于处理null值作为键并将其处理为特殊情况。
编辑
来自Hashtable
JavaDoc:
为了成功地从Hashtable中存储和检索对象,用作键的对象必须实现hashCode方法和equals方法。
由于null
不是一个对象,因此无法在其上调用.equals()
或.hashCode()
,因此Hashtable
无法计算哈希以将其用作键。
ConcurrentHashMap
是一个较新的类,但它也有不允许使用null键或值的限制。出于性能原因,他们添加了这个限制,因为支持null键和值需要额外的工作量,但大多数情况下并没有用处。 - DaoWenif (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的歧义问题。默认的Hashtable实现有空指针检查,会抛出空指针异常。后来Java开发人员可能意识到了空键(例如某些默认值)和值的重要性,因此引入了HashMap。
对于HashMap,如果键为空,则会进行空检查,然后将该元素存储在不需要哈希码的位置上。
因为在哈希表中,当您放置元素时,它会考虑键和值的哈希。基本上你会得到像这样的东西:
```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个索引中。
value.hashCode()
。 - rustyxHashtable是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值。
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
。
哈希表是一个非常古老的类,从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
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 不是对象,因此它无法实现这些方法。
哈希表 - 不允许空键
这是因为,在 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 个索引中。