1、概述
HashTable是一个线程安全的哈希表,它通过使用synchronized关键字对方法进行加锁,从而保证了线程安全,但这也导致了在单线程环境中效率低下等问题。HashTable与HashMap不同,他不允许插入null值和null键。
2、属性
//哈希表
private transient Entry<?,?>[] table;
//记录哈希表中键值对的个数
private transient int count;
//扩容的阈值
private int threshold;
//负载因子
private float loadFactor;
3、方法
构造方法
public Hashtable(intinitialCapacity, floatloadFactor) {....} public Hashtable(intinitialCapacity) { this(initialCapacity, 0.75f); } public Hashtable() { this(11, 0.75f); }
从构造函数中,我们可以获取到这些信息:HashTable默认的初始容量为11(与HashMap不同)。负载因子默认为0.75(与HashMap相同)而正因为默认初始化容量的不同,同时也没有对容量做调整的策略,所以可以先推断出,Hashtable使用的哈希函数跟HashMap是不一样的
get方法
跟HashMap相比,Hashtable的get方法非常简单。get方法使用了synchronized来修饰,所以它能保证线程安全。并且它是通过链表的方式来处理冲突的。另外,HashTable并没有像HashMap那样封装一个哈希函数,而是直接把哈希函数写在了方法中。而哈希函数也是比较简单的,它仅对哈希表的长度进行了取模。
put方法
put方法一开始就表明了不能有null值,否则就会抛出一个空指针异常。Hashtable的put方法也是使用synchronized来修饰。在Hashtable中,几乎所有的方法都使用了synchronized来保证线程安全。
rehash方法
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// 扩容为原来的两倍+1
int newCapacity = (oldCapacity << 1) + 1;
// 判断是否超过最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
// 计算下一次rehash的阈值
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
// 把旧哈希表的键值对重新哈希到哈希表中
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
Hashtable的rehash方法相当于HashMap的resize方法。跟HashMap那种巧妙的rehash方式相比,Hashtable的rehash过程需要对每个键值对都重新计算哈希值,而比起异或和与操作,取模是一个非常耗时的操作,所以这也是导致效率较低的原因之一