A few points
- The underlying data structure of HashTable is based on an array of linked lists (O (n));
- HashTable does not allow empty key and empty value;
- HashMap elements are not sorted according to the order in which they are written, but are sorted according to the hash of the Key, taking n modulo (algorithm optimization uses (n-1) & hash)
- HashTable is a thread-safe class, but it uses synchronized directly on the method, which uses blocking internal locks (locking the entire table) to ensure thread safety, and its concurrency efficiency is low. Consider the Concurrent package, such as ConcurrentHashMap;
- Hashtable, like HashMap, has two parameters that affect performance, the initial capacity and the load factor, and the load factor is also 0.75 by default;
- The initial capacity initialCapacity is 11, it is not required to be an exponential multiple of 2, not 16 of HashMap;
- The hash algorithm directly uses the hashcode of the object (the hash code of the key), without the optimization of HashMap (16-bit XOR);
- Hashtable creates an array during initialization, HashMap is lazy loading;
- The positioning algorithm is (e.hash & 0x7FFFFFFF)% tab.length. Since the number of buckets in the HashMap must be an exponential multiple of 2, the method of obtaining the bucket index can be optimized as hash & (length-1);
- The insertion efficiency of Hashtable is inefficient. Each insertion has to traverse the linked list once. Each time is O (n), the efficiency is lower than HashMap. HashMap is generally O (1) for direct positioning, and O (1) for a small number of elements in the red-black tree LogN), O (n) of a small number of elements in the linked list, the linked list is less than 8;
- Hashtable read efficiency is also low, each read must traverse the linked list once, each time is O (n), HashMap is almost O (1), directly locate h & (length-1), plus red and black O (LogN), so it is almost O (1);
Class definition
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
Attributes
- Entry <?,?> [] Table: Entry type array, used to store key-value pairs in Hashtable;
- int count: how many key-value pairs are stored in the hashtable
- int threshold: when the count value is greater than this value, the hash table expands the capacity and rehash ()
- float loadFactor: threshold = initial size of hash table * loadFactor, initial capacity defaults to 11, loadFactor defaults to 0.75
- int modCount: the number of times this HashTable structure has been modified. This value is incremented every time an element is added, updated, or deleted. Implement the "fail-fast" mechanism. When iterating on the Hashtable in a concurrent collection, if other threads make structural modifications to the Hashtable, the iterator will compare the expectedModCount and modCount to see if they are consistent. If they are not consistent, a ConcurrentModificationException will be thrown.
Constructor
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); }
modCount consistency
public static void main(String[] args) { Hashtable<Integer, String> tb = new Hashtable<Integer,String>(); tb.put(1, "BUPT"); tb.put(2, "PKU"); tb.put(3, "THU"); Iterator<Entry<Integer, String>> iter = tb.entrySet().iterator(); while(iter.hasNext()){ Entry<?, ?> entry = (Entry<?, ?>) iter.next(); //此处会抛出异常 System.out.println(entry.getValue()); if("THU ".equals(entry.getValue())){ tb.remove(entry.getKey()); } } } /* 输出结果如下: THU Exception in thread "main" java.util.ConcurrentModificationException at java.util.Hashtable$Enumerator.next(Hashtable.java:1367) at ali.Main.main(Main.java:16) */
ConcurrentModificationException exception, the value of modCount is updated every time the data in the hashtable is modified, and the next method of the iterator Enumerator <T> will judge modCount! = ExpectedModCount
public T Next () { // first determine whether modCount and equal expectedModCount // Since Hashtable object in the main () method changes the value modCount by tb.remove, such that the unequal modCount expectedModCount and thrown // solution The way is to replace the tb.remove () method with the iter.remove () method if (modCount! = ExpectedModCount ) throw new ConcurrentModificationException (); return nextElement (); }
// This method modifies modCount and expectedModCount while removing the element The value of public void remove () { if (! Iterator) throw new UnsupportedOperationException(); if (lastReturned == null) throw new IllegalStateException("Hashtable Enumerator"); if (modCount != expectedModCount) throw new ConcurrentModificationException(); synchronized(Hashtable.this) { Entry<?,?>[] tab = Hashtable.this.table; int index = (lastReturned.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 == lastReturned) { modCount++; expectedModCount++; if (prev == null) tab[index] = e.next; else prev.next = e.next; count--; lastReturned = null; return; } } throw new ConcurrentModificationException(); } }
Main method
put
It can be seen that each insert must traverse the linked list once, each time is O (n), the efficiency is lower than HashMap, HashMap is generally O (LogN) in the red-black tree, O (n) in the linked list, direct positioning Is 1
public synchronized V put (K key, V value) { // The value is not allowed to be null if (value == null ) { throw new NullPointerException (); } // Makes sure the key is not already in the hashtable. Entry <? ?,> Tab [] = Table; // get key hash int hash = key.hashCode (); // get the index corresponding to the hash bucket in the array int index = (hash & 0x7FFFFFFF)% tab.length; @SuppressWarnings ( "unchecked" ) // Get the head of the linked list in the bucket Entry <K, V> entry = (Entry <K, V> ) tab [index]; // Traversing from the beginning for (; entry! = Null ; entry = entry.next) { // Once the hash values are equal and the keys are equal , Replace the old value if ((entry.hash == hash) && entry.key.equals (key)) { V old = entry.value; entry.value = value; return old; } } // if the same key is not found , Then add a new node addEntry (hash, key, value, index); return null ; }
addEntry
private void addEntry(int hash, K key, V value, int index) { modCount++; Entry<?,?> tab[] = table; //如果尺寸超过了阈值,进行rehash 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++; }
get
public synchronized V get (Object key) { Entry <?,?> tab [] = table; int hash = key.hashCode (); int index = (hash & 0x7FFFFFFF)% tab.length; // traverse all elements, ha If the value is the same as the key, return for (Entry <?,?> E = tab [index]; e! = Null ; e = e.next) { if ((e.hash == hash) && e.key. equals (key)) { return (V) e.value; } } return null ; }