介紹
- 和HashMap一樣,Hashtable 也是一個散列表,它存儲的內容是鍵值對(key-value)映射,底層實現由“數組+鏈表”實現,相對於hashMap來說簡單很多。
- Hashtable 繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable接口。
- Hashtable 的函數都是同步的,這意味着它是線程安全的。它的key、value都不可以爲null。
- Hashtable中的映射不是有序的
- 不建議使用,以後說不定哪天就廢掉了。連官方文檔也說了,如果在非線程安全的情況下使用,建議使用HashMap替換,如果在線程安全的情況下使用,建議使用ConcurrentHashMap替換。
屬性
public class Hashtable<K,V> extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
// Hashtable保存數據的數組
private transient Entry<?,?>[] table;
// hashtable的容量
private transient int count;
// 閾值
private int threshold;
// 負載因子
private float loadFactor;
// 結構性修改
private transient int modCount = 0;
}
構造函數
//指定初始容量和加載因子構造函數
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);
}
/**
指定初始容量和默認加載因子(0.75)構造函數
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
默認構造函數,初始容量11,加載因子0.75
*/
public Hashtable() {
this(11, 0.75f);
}
/**
給定的map具有相同映射關係的新的hash表
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
添加操作
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();//獲取key的hashCode
int index = (hash & 0x7FFFFFFF) % tab.length;//確認key的索引位置
//根據索引找到所處的位置
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//循環查找,尋找key,並替換
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;
}
private void addEntry(int hash, K key, V value, int index) {
Entry<?,?> tab[] = table;
//判斷是否需要擴容
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
//將元素插入到對應的鏈表第一個位子上 直接加 不需要判斷是否存在 調用的地方判斷
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
modCount++;
}
//擴容方法
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//擴容大小是原來的2倍+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++;
//計算下次擴容的門檻數量
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;
}
}
}
putAll方法和put方法類似,不再做具體介紹
public synchronized void putAll(Map<? extends K, ? extends V> t) {
//依次將數據,添加到映射關係中
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
}
刪除操作
//刪除置頂key的元素,相對於hashMap簡單很多
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
//循環找到對應的元素,並刪除
for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
modCount++;
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}
查找操作
//很簡單,不在介紹
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;
}
總結
- 首先,Hashtable底層是通過數組加鏈表實現的,這點和JDK1.8之前的HashMap差不多。
- 其次,Hashtable是不允許key或者value爲null的。
- 並且,Hashtable的計算索引方法,默認容量大小,擴容方法都與HashMap不太一樣。
- 其實我們可以看到,Hashtable之所以線程安全,大部分方法都是使用了synchronized關鍵字,雖然JDK優化了synchronized,但在方法上使用該關鍵字,無疑仍舊是效率低下的操作。就這方面來說,ConcurrentHashMap無疑比Hashtable好多了,後續會有專門文章介紹ConcurrentHashMap,這裏就不多說了。
總之呢,Hashtable無疑算是廢掉了,說不定過不了多久,它就消失在Map框架中了呢。