大多数同步和并发集合(如 HashTable、ConcurrentHashMap 等)都不允许空值。空元素有什么具体问题吗?
5 回答
HashTable.get(key)
如果指定的键不存在于 HashTable 中,则方法返回 null。如果 HashTable 允许null
作为值,则如果我从HashTable.get(key)
方法中获取 null,则可能有两种可能性。
- 哈希表中不存在密钥
- 键存在,但值集为空
API 的用户可能会感到困惑。我相信他们不允许空值只是为了防止这种歧义。
Hashtable 有点过时,所以我不会对此发表评论。至于ConcurrentHashMap
API 与标准的重要补充之一HashMap
是一些原子方法,例如putIfAbsent
. Java 文档:
返回与指定键关联的前一个值,如果该键没有映射,则返回 null
特别是,如果映射允许空键,则该方法使用起来会复杂得多。需要确保值不能被覆盖的典型模式是:
ConcurrentMap<K,V> map = new ConcurrentHashMap<> ();
V value = map.get(key);
if (value == null) {
value = new V();
V previousValue = map.putIfAbsent(key, value);
if (previousValue != null) { //Here you need to be sure what that means
value = previousValue;
}
}
useValue(value);
另一个示例是如何检查键是否在 HashMap 中(并且您需要返回值):
V value = map.get(key);
if (value == null && !map.containsKey(key)) {
}
并发环境中的问题是整个事情不是原子的。
另见这篇文章和CHM 作者的这些评论。
这是一个接受空键和值的同步映射
Map m = Collections.synchronizedMap(new HashMap());
从哈希表 JavaDoc:
要成功地从哈希表中存储和检索对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
简而言之,由于 null 不是对象,因此您不能对其调用 .equals() 或 .hashCode() ,因此 Hashtable 无法计算哈希以将其用作键。
HashMap 较新,并且具有更高级的功能,基本上只是对 Hashtable 功能的改进。因此,在创建 HashMap 时,它专门设计为将 null 值作为键处理,并将它们作为特殊情况处理。
具体来说,在发出 .get(key) 时,使用 null 作为键的处理方式如下:
(key==null ? k==null : key.equals(k))
资料来源:为什么 Hashtable 不采用空键?
当您同步时,这意味着您将获得对该对象或某个部分的锁定。如果您的对象为空,那么您将如何决定您将在哪个部分获得锁定?
说在concurrenthashmaps中,你可以把它分成16个不同的锁。现在你将如何决定将 null 放置在哪里?
Null 没有附加值,它只是意味着没有对象。因此,为了避免歧义,最好不要拥有它。