【JDK1.8源碼閱讀】Hashtable.class(完)

在看hashtable前你需要了解

Hashtable本質是一條線性的鏈表:
0、Hashtable是較早的JDK版本中的類,數據結構是`Entry<K, V> next`單向鏈表
1、哈希函數(直接尋址法、數字分析法、平方取中法、摺疊法、隨機數法、除留餘數法)
2hashCode()的生成在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。

Hashtable比HashMap出生早

Hashtable命名沒用駝峯規則,HashMap用上了。Hashtable現在項目使用頻率很低。

參考:https://blog.csdn.net/u010297957/article/details/51974340

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章