JDK1.8源碼閱讀Hashtable.class
在看hashtable前你需要了解
Hashtable本質是一條線性的鏈表:
1、哈希函數(直接尋址法、數字分析法、平方取中法、摺疊法、隨機數法、除留餘數法)
2、hashCode()的生成在Object類內置了方法
3、鏈表Entry<?, ?>[]的數據結構
4、如何插入和刪除一個Entry
5、與HashMap的區別
祖先Dictionary(繼承)
繼承Dictionary<K,V>,本是抽象的key、value。內部全是抽象變量。
public abstract
class Dictionary<K,V> {
public Dictionary() { }
abstract public int size();
abstract public boolean isEmpty();
abstract public Enumeration<K> keys();
abstract public Enumeration<V> elements();
abstract public V get(Object key);
abstract public V put(K key, V value);
abstract public V remove(Object key);
}
兄弟Map,Cloneable,Serializable(實現接口)
Cloneable和Serializable都是一個空接口。
- Map<K, V>接口:定義變量的常規操作方法
- Cloneable接口:clone是首先分配內存,分配的內存與調用clone方法對象的內存相同,然後將源對象中各個變量的值,填充到新的對象中,填充完成後,clone方法返回一個新的地址,這個新地址的對象與源對象相同,只是地址不同。
- Serializable接口:賦予序列化能力
孩子hash、key、value
- 從數據結構可以看出Hashtable是由Entry<K, V>組成的單向鏈表
private static class Entry<K, V> implements Map.Entry<K, V> {
final int hash;//使用key的hashCode(),是Object類的native的方法
final K key;
V value;
Entry<K, V> next;
protected Entry(int hash, K key, V value, Entry<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
- 其它常量表述的含義
private transient Entry<?, ?>[] table;//看1108行的數據結構
private transient int count;//統計Entry的數量
private int threshold;//addEntry的時候如果比count小,則調用rehash()增加容量,重新計算每個鍵值對的hashCode
private float loadFactor;
private transient int modCount = 0;
private static final long serialVersionUID = 1421746759512286392L;
- table:鍵值對數組,每個Entry本質上是一個單向鏈表的表頭
- count:當前表中的Entry數量,如果超過了閾值,就會擴容,即調用rehash方法
- threshold:rehash閾值
- loadFactor:負載因子
- modCount :用來實現"fail-fast"機制的(也就是快速失敗)。所謂快速失敗就是在併發集合中,其進行迭代操作時,若有其他線程對其進行結構性的修改,這時迭代器會立馬感知到,並且立即拋出ConcurrentModificationException異常,而不是等到迭代完成之後才告訴你(你已經出錯了)。
- serialVersionUID:版本序列號,序列化用的
究竟如何插入一個值
- 確保key不在table,如果key對應的值存在,則用新值覆蓋value
- 如果key對應的值不存在,就調用addEntry方法加入到index位置
public synchronized V put(K key, V value) {
if (value == null) {
throw new NullPointerException();
}
//確保key不在table,如果key對應的值存在,則用新值覆蓋value
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;
}
}
// 如果key對應的值不存在,就調用addEntry方法加入
addEntry(hash, key, value, index);
return null;
}
- tab[index]和HashMap不同,Hashtable選擇把新插入的元素放到鏈表最前邊
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?, ?> tab[] = table;//重新增加一個數組
if (count >= threshold) {
rehash();// 當前元素大於等於閾值,就擴容並且再計算hash值
tab = table;
hash = key.hashCode();
//0x7FFFFFFF 0111 1111 1111 1111 1111 1111 1111 1111
index = (hash & 0x7FFFFFFF) % tab.length;//第一位是0表示正數,0與任何數都是0,1與任何數都是1,因此以此保證第一位是0,其後不變
}
@SuppressWarnings("unchecked")
Entry<K, V> e = (Entry<K, V>) tab[index];// Creates the new entry.
//和HashMap不同,Hashtable選擇把新插入的元素放到鏈表最前邊,而且沒有使用紅黑樹
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
究竟刪除插入一個值
- e是要刪除的節點,遍歷鏈表,如果表中
(e.hash == hash) && e.key.equals(key)
則表示存在相同的節點,將其刪除(前一個節點的next指向下一個節點)
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];//e是要刪除的節點
for (Entry<K, V> prev = null; e != null; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}
你應該知道的
HashTable的key/value爲什麼不可爲null
也許Hashtable類的設計者當時認爲null作爲key 和value 是沒有什麼用的。
HashMap對象的key、value值均可爲null。
HashTable對象的key、value值均不可爲null。
參考:https://blog.csdn.net/u010297957/article/details/51974340