HashMap与Hashtable的区别
一.类继承体系
HashMap继承自抽象类AbstractMap,而Hashtable继承自抽象类Dictionary。其中Dictionary已经被放弃,HashTable比HashMap多了两个公开方法,一个是elements(),另一个是contains()。
二.Null Key 与 Null Value
HashMap支持null键和null值的,而Hashtable在遇到null时,会抛出NullPointerException异常。
HashMap在实现时对null值做了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket中。
//Hashtable
public synchronized V put(K key, V value) {
// 如果value为null,抛出NullPointerException
if (value == null) {
throw new NullPointerException();
}
// 如果key为null,在调用key.hashCode()时抛出NullPointerException
// ...
}
//HasMap
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
// 当key为null时,调用putForNullKey特殊处理
if (key == null)
return putForNullKey(value);
// ...
}
private V putForNullKey(V value) {
// key为null时,放到table[0]也就是第0个bucket中
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
三.初始容量及扩容机制
Hashtable默认的初始大小为11,之后每次扩充为原来的2n+1.
HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。
// Hashtable哈希表默认初始大小为11
public Hashtable() {
this(11, 0.75f);
}
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// 每次扩容为原来的2n+1
int newCapacity = (oldCapacity << 1) + 1;
// ...
}
以下代码及注释来自java.util.HashMap
// 哈希表默认初始大小为2^4=16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
void addEntry(int hash, K key, V value, int bucketIndex) {
// 每次扩充为原来的2n
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
}
Hashtable会尽量使用素数,而HashMap则总是使用2的幂作为哈希表的大小。当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。在取模运算时,如果模数是2的幂,则可以直接使用位运算来得到结果,效率高。
四.线程安全
Hashtable是线程安全的,HashMap不是线程安全的。
以下代码及注释来自HashTable
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
public Set<K> keySet() {
if (keySet == null)
keySet = Collections.synchronizedSet(new KeySet(), this);
return keySet;
}
Hashtable公开的方法都用了synchronized描述符。
最后
如果你不需要线程安全,那么使用HashMap,如果需要使用线程安全,那么使用ConcurrentHashMap。HashTable已经被淘汰了。
参考: ImportNew