標籤:check n+1 表示 hash函數 碰撞 安全 bucket void 初始
建構函式
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); }
分析:
Hashtable(int initialCapacity, float loadFactor)建構函式預設容量是11,載入因子(負載因子)0.75,如果第四個建構函式,那麼就取2*size與11更大的那個作為初始容量。我們知道HashMap的容量一定是2整數次冪,用的初始容量為16,HashTable為啥用11為預設初始容量?我們先帶著這個問題繼續往下看。
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()); } 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; } 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<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; } 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<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; } } }
分析:
int index = (hash & 0x7FFFFFFF) % tab.length;這句話是做什麼用的呢?(hash & 0x7FFFFFFF) 的&位元運算是為了確保是一個正整數,而hash是一個有符號數。如果hash值相同,那麼再去比較key是不是相等,如果相等那麼替換old值,並返回old值。如果沒有找到相同的key,那麼說明不同的key發生了碰撞,那麼就利用鏈地址法解決這個問題,把新元素串連到該index後面的尾部。在串連到index後面之前,是否進行擴容的判斷,如果達到臨界值,那麼進行擴容,一旦擴容之後需要重新構建hash表,int newCapacity = (oldCapacity << 1) + 1;這句表示擴容後的大小(2n+1),然後把原來的hashtable值拷貝到新hashtable中,從這裡我們就可以看出,hashtable選用的就是通過利用奇數作為容量,那麼這麼做是否能夠讓元素分布更加均勻呢?如果有知道的同學可以跟我分析你的看法。
總結:
1.HashTable是安全執行緒的
2.HashTable底層實現跟HashMap一樣,使用的是鏈表數組。
3.HashTable的擴容公式是:2n+1,預設容量11或者2*size,選用奇數作為容量。
4.HashTable中沒有hash函數了,直接取的是Object.hashCode值作為hash值
5.HashTable中沒有再做類似HashMap中的擾動來降低碰撞機率
源碼之HashTable