Hashtable
Map, Cloneable, Serializable
–>Dictionary
本文中的Hashtable主要与HashMap进行对比,其中有两个主要的区别,一是Hashtable中提供的公共方法中都由synchronized修饰,因此是保证线程安全的,而HashMap并未保证线程安全性。但Hashtable仍具有fail-fast特性,在得到iterator之后,若通过该迭代器之外的方式修改了表结构,再次调用该迭代器的方法时会抛出ConcurrentModificationException。第二是Hashtable的key值和value值不能为null(1.8中的方法仅要求value不能为null),而HashMap是可以为null的。在Hashtable中也使用桶这一概念,并且出现hash冲突时使用链表解决冲突。
初始化默认值
Hashtable的初始化默认值是直接写死在构造函数中的。
// 桶的最大大小,减8是因为很多时候数组还需要一部分空间存储本身的信息
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
public Hashtable() {
this(11, 0.75f);
}
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
若参数initialCapacity输入0,则初始化桶大小为1。和HashMap等容器不同,Hashtable是在初始化的时候就给table分配了内存空间。
成员变量
// table中的每一个元素代表一个桶
private transient Entry<?,?>[] table;
// 已储存的元素个数
private transient int count;
// 定义为(capacity(table.size) * loadFactor),count超过该值即重新分配空间
private int threshold;
// 负载(默认值或赋新值)
private float loadFactor;
// 实现fail-fast
private transient int modCount = 0;
桶定位和扩容方式
桶定位
Hashtable中根据key值计算hash值的方法为直接调用Object.hashCode计算。但计算桶标号的方法和HashMap不同,是使用了hash值的后31位与桶的数量取了一个模值运算。
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
扩容
与HashMap的直接扩大两倍不同,Hashtable是将桶数量扩大为当前数量的两倍再加上1,保证桶数量是奇数(最好是保证是素数,但不太好实现,因此仅保证了奇数条件)。
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
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++;
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;
}
}
}
entrySet,keySet,Values
得到这三个Set的方法类似,都是通过调用Collections.synchronizedCollection方法实现,将当前Hashtable对象作为锁,保证使用这三个Set时的线程安全问题。
private transient volatile Set<K> keySet;
private transient volatile Set<Map.Entry<K,V>> entrySet;
private transient volatile Collection<V> values;
keySet = Collections.synchronizedSet(new KeySet(), this);
entrySet = Collections.synchronizedSet(new EntrySet(),this);
values = Collections.synchronizedCollection(new ValueCollection(),this);
枚举器,迭代器
Hashtable中得到枚举器和迭代器都是通过Enumerator类实现,可以看到,Enumerator实现了枚举器和迭代器两个接口,并通过iterator标识确认返回的是枚举器还是迭代器。同时,通过type确定需要返回的是key,value或entry的枚举器/迭代器。
// Types of Enumerations/Iterations
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;
private class Enumerator<T> implements Enumeration<T>, Iterator<T>{
Entry<?,?>[] table = Hashtable.this.table;
int index = table.length;
Entry<?,?> entry;
Entry<?,?> lastReturned;
int type;
/**
* Indicates whether this Enumerator is serving as an Iterator
* or an Enumeration. (true -> Iterator).
*/
boolean iterator;
/**
* The modCount value that the iterator believes that the backing
* Hashtable should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
protected int expectedModCount = modCount;
}