Java源碼之Hashtable
一、Hashtable概述
類實現一個雜湊表,該雜湊表將鍵key對象映射到相應的值value對象。要求key和value都非null。為了成功地在雜湊表中儲存和擷取對象,用作鍵的對象必須實現 hashCode 方法和 equals 方法。
Hashtable是線程同步的,但是非線程同步的HashMap完全可以取代它。
如果不需要安全執行緒,可以直接使用HashMap取代;
如果需要安全執行緒高並發,可以使用java.util.concurrent.ConcurrentHashMap取代。
二、Hashtable資料結構
Hashtable與jdk1.8之前的HashMap一樣,是用數組+鏈表實現。
/** * Hashtable數組衝突鏈結點 */private static class Entry implements Map.Entry { final int hash; final K key; V value; Entry next; // 下一個結點 protected Entry(int hash, K key, V value, Entry 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) 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(); }}
三、Hashtable源碼
1.標頭檔
package java.util;import java.io.*;import java.util.concurrent.ThreadLocalRandom;import java.util.function.BiConsumer;import java.util.function.Function;import java.util.function.BiFunction;
2.實現與繼承
public class Hashtableextends Dictionaryimplements Map, Cloneable, java.io.Serializable
3.屬性
/** * hash表數組 */private transient Entry[] table;/** * 數組中儲存的元素個數 */private transient int count;/** * 閾值,超過這個值數組要擴容 * threshold = capacity * loadFactor */private int threshold;/** * 裝載因子 */private float loadFactor;/** * 修改次數 * 採用fail-fast機制 */private transient int modCount = 0;
4.構造器與方法
這部分與HashMap的主要區別是,hash函數的演算法。
這裡用的是典型的除留取餘法:
int index = (hash & 0x7FFFFFFF) % tab.length;
這部分的構造器、方法與HashMap的差別不大,只是在方法前面加了synchronized使方法同步。
/** * 構造方法一: * 用指定容量 + 指定裝載因子構造 */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);}/** * 構造方法三: * 使用預設容量11 + 預設裝載因子 */public Hashtable() { this(11, 0.75f);}/** * 構造方法四: * 使用Map構造 */public Hashtable(Map t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t);}/** * 返回容量大小 */public synchronized int size() { return count;}/** * 判空 */public synchronized boolean isEmpty() { return count == 0;}/** * 返回所有key值的枚舉集合 */public synchronized Enumeration keys() { return this.getEnumeration(KEYS);}/** * 返回所有value值的枚舉集合 */public synchronized Enumeration elements() { return this.getEnumeration(VALUES);}/** * 判斷是否包含value值對象 */public synchronized boolean contains(Object value) { if (value == null) { throw new NullPointerException(); } Entry tab[] = table; for (int i = tab.length ; i-- > 0 ;) { for (Entry e = tab[i] ; e != null ; e = e.next) { if (e.value.equals(value)) { return true; } } } return false;}/** * 判斷是否包含value值對象 */public boolean containsValue(Object value) { return contains(value);}/** * 判斷是否包含key索引值對象 */public synchronized boolean containsKey(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 true; } } return false;}/** * 擷取索引值key對應的value */@SuppressWarnings("unchecked")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;}/** * 規定的最大數組容量 */private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/** * 擴容(2*oldCap + 1) */@SuppressWarnings("unchecked")protected void rehash() { int oldCapacity = table.length; Entry[] oldMap = table; // overflow-conscious code 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 old = (Entry)oldMap[i] ; old != null ; ) { Entry e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry)newMap[index]; newMap[index] = e; } }}private void addEntry(int hash, K key, V value, int index) { modCount++; 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 e = (Entry) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++;}/** * 添加元素 * 原key索引值存在,返回原key鍵對應的value * 原key索引值不存在,返回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 entry = (Entry)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;}/** * 刪除並返回要刪除的value */public synchronized V remove(Object key) { Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry e = (Entry)tab[index]; for(Entry 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;}/** * 將Map中的元素添加進來 */public synchronized void putAll(Map t) { for (Map.Entry e : t.entrySet()) put(e.getKey(), e.getValue());}/** * 清空 */public synchronized void clear() { Entry tab[] = table; modCount++; for (int index = tab.length; --index >= 0; ) tab[index] = null; count = 0;}/** * 複製對象 */public synchronized Object clone() { try { Hashtable t = (Hashtable)super.clone(); t.table = new Entry[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); }}private Enumeration getEnumeration(int type) { if (count == 0) { return Collections.emptyEnumeration(); } else { return new Enumerator<>(type, false); }}// 獲得迭代器private Iterator getIterator(int type) { if (count == 0) { return Collections.emptyIterator(); } else { return new Enumerator<>(type, true); }}// Views/** * Each of these fields are initialized to contain an instance of the * appropriate view the first time this view is requested. The views are * stateless, so there's no reason to create more than one of each. */private transient volatile Set keySet;private transient volatile Set> entrySet;private transient volatile Collection values;/** * 返回包含此map的Set視圖 * 通過Set視圖可獲得迭代器Iterator對象,對map進行迭代 */public Set> entrySet() { if (entrySet==null) entrySet = Collections.synchronizedSet(new EntrySet(), this); return entrySet;}// Set視圖類private class EntrySet extends AbstractSet> { public Iterator> iterator() { return getIterator(ENTRIES); } public boolean add(Map.Entry o) { return super.add(o); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry)o; Object key = entry.getKey(); 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.equals(entry)) return true; return false; } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry) o; Object key = entry.getKey(); Entry[] tab = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry e = (Entry)tab[index]; for(Entry prev = null; e != null; prev = e, e = e.next) { if (e.hash==hash && e.equals(entry)) { modCount++; if (prev != null) prev.next = e.next; else tab[index] = e.next; count--; e.value = null; return true; } } return false; } public int size() { return count; } public void clear() { Hashtable.this.clear(); }}/** * Returns a {@link Collection} view of the values contained in this map. * The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. If the map is * modified while an iteration over the collection is in progress * (except through the iterator's own remove operation), * the results of the iteration are undefined. The collection * supports element removal, which removes the corresponding * mapping from the map, via the Iterator.remove, * Collection.remove, removeAll, * retainAll and clear operations. It does not * support the add or addAll operations. * * @since 1.2 */public Collection values() { if (values==null) values = Collections.synchronizedCollection(new ValueCollection(), this); return values;}private class ValueCollection extends AbstractCollection { public Iterator iterator() { return getIterator(VALUES); } public int size() { return count; } public boolean contains(Object o) { return containsValue(o); } public void clear() { Hashtable.this.clear(); }}// Comparison and hashing/** * 實現equals * 判等 */public synchronized boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map t = (Map) o; if (t.size() != size()) return false; try { Iterator> i = entrySet().iterator(); while (i.hasNext()) { Map.Entry e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null) { if (!(t.get(key)==null && t.containsKey(key))) return false; } else { if (!value.equals(t.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true;}/** * 實現hashCode */public synchronized int hashCode() { /* * This code detects the recursion caused by computing the hash code * of a self-referential hash table and prevents the stack overflow * that would otherwise result. This allows certain 1.1-era * applets with self-referential hash tables to work. This code * abuses the loadFactor field to do double-duty as a hashCode * in progress flag, so as not to worsen the space performance. * A negative load factor indicates that hash code computation is * in progress. */ int h = 0; if (count == 0 || loadFactor < 0) return h; // Returns zero loadFactor = -loadFactor; // Mark hashCode computation in progress Entry[] tab = table; for (Entry entry : tab) { while (entry != null) { h += entry.hashCode(); entry = entry.next; } } loadFactor = -loadFactor; // Mark hashCode computation complete return h;}@Overridepublic synchronized boolean replace(K key, V oldValue, V newValue) { Objects.requireNonNull(oldValue); Objects.requireNonNull(newValue); Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry e = (Entry)tab[index]; for (; e != null; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { if (e.value.equals(oldValue)) { e.value = newValue; return true; } else { return false; } } } return false;}/* * 替換 */@Overridepublic synchronized V replace(K key, V value) { Objects.requireNonNull(value); Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry e = (Entry)tab[index]; for (; e != null; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { V oldValue = e.value; e.value = value; return oldValue; } } return null;}