一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
本文主要降并发的Hashtable的一些特性,主要是和HashMap来对比。
支持并发
Hashmap不支持并发,为了支持并发,Java引入了Hashtable。 Hashtable使用synchronized来修饰方法保证get和put操作的并发安全,能解决上文提到的HashMap死循环问题。
值不为null
Hashtable不支持null的KV。
这是为什么呢? hashmap是支持的,我们看下这段代码:
HashMap hashMap = new HashMap<>();
hashMap.put(1,null);
Object v1 = hashMap.get(1);
System.out.println("v1 = " + v1);
Object v2 = hashMap.get(2);
System.out.println("v2 = " + v2);
v1 = null
v2 = null
这是因为如果返回的值是null,那究竟是真的是value=null,还是key不存在呢? 可见HashMap还存在语义模糊的问题。
所以HashTable做了这一点优化,value不能为null. 如果value为null,则在put函数开头的时候快速失败( Make sure the value is not 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;
}
Key不为空
key为null有些不方便的地方,虽然HashMap支持key为null,但是要特殊判断,不可以使用统一规则hashcode来计算这个key落的hash桶。如下:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
而Hashtable在实现上摒弃了null的key。
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
如果key为null,则在调用hashcode函数的时候快速失败。
但是key为null或许是业务需要的特性,Hashtable的实现就缺少了这个功能。
小总结
HashTable有如下特性:
- 解决了Hashmap的并发安全问题
- 不支持空值,解决了hashmap null值语义模糊的问题
- 不支持空key,实现更简单,但不一定适合业务场景。
- 另外Hashtable的并发方式效率太低,目前并不推荐使用。