Java集合--HashTable
簡單看一下 HashTable 內部結構
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable { private transient Entry<?,?>[] table; 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); } public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } public Hashtable() { this(11, 0.75f); } public Hashtable(Map<? extends K, ? extends V> t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); } public synchronized int size() {...} public synchronized boolean isEmpty() {...} public synchronized Enumeration<K> keys() {...} public synchronized Enumeration<V> elements() {...} public synchronized boolean contains(Object value) {...} public synchronized boolean containsKey(Object key) {...} private static class Entry<K,V> implements Map.Entry<K,V> { final int hash; 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; } @SuppressWarnings("unchecked") protected Object clone() { return new Entry<>(hash, key, value, (next==null ? null : (Entry<K,V>) next.clone())); } // Map.Entry Ops public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { if (value == null) throw new NullPointerException(); V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return (key==null ? e.getKey()==null : key.equals(e.getKey())) && (value==null ? e.getValue()==null : value.equals(e.getValue())); } public int hashCode() { return hash ^ Objects.hashCode(value); } public String toString() { return key.toString()+"="+value.toString(); } } 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; } 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(); 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; } } addEntry(hash, key, value, index); return null; } 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)) { 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; } public synchronized void putAll(Map<? extends K, ? extends V> t) {...} public synchronized void clear() {...} public synchronized Object clone() {...} public synchronized String toString() {...} .... }
看過 HashMap 內部結構之後再來看 HashTable,就會發現結構非常相似。
預設構造方法初始化容量為11,負載因子為0.75,幾乎所有的方法都用了 synchronized 修飾了,所有的操作都需要把整個map鎖起來,資料是非常安全的,在多執行緒情況下不會有什麼問題,但是效能應該也不怎麼樣。
HashTable 確定一個數組的位置用的方法為
int index = (hash & 0x7FFFFFFF) % tab.length;
hash 值可能為負數,0x7FFFFFFF的意思是首位為0 剩下的都是1。二進位制的第一位為符號為,hash & 0x7FFFFFFF % tab.length 的意思也就是把hash值轉換為正數在和tab.length取模,得到陣列的位置。
get/put/remove 都是先鎖方法,然後通過這個方法獲取位置,再對位置上的節點操作。
Java Collections Framework</a>. Unlike the new collection
implementations, {@code Hashtable} is synchronized. If a
thread-safe implementation is not needed, it is recommended to use
{@link HashMap} in place of {@code Hashtable}. If a thread-safe
highly-concurrent implementation is desired, then it is recommended
to use {@link java.util.concurrent.ConcurrentHashMap} in place of
{@code Hashtable}.
這是官網註解,如果不需要執行緒安全的話,用 HashMap。如果需要執行緒安全的話,用 ConcurrentHashMap。