JDK11-HashTable集合

介紹

  •  和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框架中了呢。
 

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