Hashtable
1. Contrast HashMap
1.
project | HashMap | Hashtable |
Default capacity | 16 | 11 |
load factor | 0.75 | 0.75 |
null value | Allow key/value to be empty | Do not allow key/value to be empty (null pointer exception during put operation) |
Expansion | length*2 | length*2+1 |
Safety | thread unsafe | thread safety |
extends | AbstractMap | Dictionary |
capacity | >=1 | 2^n |
2. The load factor is
too low , the space utilization is low, the frequent expansion is
too high, the addressing time increases, and the key value is too much;
3. The fast failure
modCount
is in the traversal process, if modCount > expectedCount
throw new ConcurrentModificationException
2. Class definition
1. Source code
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
3. Properties
1. Source code
/** * The hash table data. */ // array of Entry, Entry private transient Entry[] table; /** * The total number of entries in the hash table. */ // capacity private transient int count; /** * The table is rehashed when its size exceeds this threshold. (The * value of this field is (int)(capacity * loadFactor).) * * @serial */ // critical value private int threshold; /** * The load factor for the hashtable. * * @serial */ // load factor private float loadFactor; /** * The number of times this Hashtable has been structurally modified * Structural modifications are those that change the number of entries in * the Hashtable or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the Hashtable fail-fast. (See ConcurrentModificationException). */ // modification times private transient int modCount = 0; /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = 1421746759512286392L;
Fourth, the construction method
1.
// Initialize capacity, load factor public Hashtable(int initialCapacity, float loadFactor) { // Initialize capacity cannot be less than 0 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); // The load factor cannot be less than 0 and it is a float type of data if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0) // Initialize the capacity to any number, HashMap must be the N power of 2 initialCapacity = 1; this.loadFactor = loadFactor; table = new Entry[initialCapacity]; threshold = (int)(initialCapacity * loadFactor); }
2.
// default load factor 0.75 public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } /** * Constructs a new, empty hashtable with a default initial capacity (11) * and load factor (0.75). */ // default initialize capacity 11 public Hashtable() { this(11, 0.75f); }
3. Take an existing collection as a parameter
/** * Constructs a new hashtable with the same mappings as the given * Map. The hashtable is created with an initial capacity sufficient to * hold the mappings in the given Map and a default load factor (0.75). * * @param t the map whose mappings are to be placed in this map. * @throws NullPointerException if the specified map is null. * @since 1.2 */ public Hashtable(Map<? extends K, ? extends V> t) { // Call the constructor, and the initialization capacity is twice the number of key-value pairs in the existing set this(Math.max(2*t.size(), 11), 0.75f); putAll(t); } // Traverse the existing collection, call the put method, synchronized keyword, thread safety 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()); }
Five, put ()
1. Source code
public synchronized V put(K key, V value) { // Make sure the value is not null // value is not allowed to be null, null pointer exception if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. // assign Entry tab[] = table; // key cannot be empty, otherwise a null pointer exception will be reported when the hashcode is obtained // A hash, the hash value of the key itself int hash = key.hashCode(); // Calculate the hash value twice, and then take the remainder with the length to determine the position stored in the array int index = (hash & 0x7FFFFFFF) % tab.length; // Take out the data at the index position, traverse the linked list under the array, and determine whether the key already exists // If it already exists, replace the existing value and return the old value for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { V old = e.value; e.value = value; return old; } } // increment the number of operations by 1 modCount++; // Whether the number count of Entry in Hashtable is greater than the critical value if (count >= threshold) { // Rehash the table if the threshold is exceeded // expand rehash(); tab = table; index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. // Assign the linked list at tab[index] position to e Entry<K,V> e = tab[index]; // Add an Entry and put the original linked list in the next of the newly added Entry // That is, the newly added Entry is placed at the top of the linked list tab[index] = new Entry<K,V>(hash, key, value, e); count++; return null; } // Constructor of Entry protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } // expand protected void rehash() { // Capacity before expansion int oldCapacity = table.length; Entry[] oldMap = table; // The expanded capacity length*2 + 1 int newCapacity = oldCapacity * 2 + 1; Entry[] newMap = new Entry[newCapacity]; // increment the number of operations by 1 modCount++; threshold = (int)(newCapacity * loadFactor); table = newMap; // Two-layer loop, the outer layer traverses the total length of the original array, and puts the old array into the new array in turn for (int i = oldCapacity ; i-- > 0 ;) { // Get the linked list at index i in the old data for (Entry<K,V> old = oldMap[i] ; old != null ; ) { // Get the element value at the current position of the linked list Entry<K,V> e = old; // pointer down old = old.next; // Calculate storage location int index = (e.hash & 0x7FFFFFFF) % newCapacity; // The current element points to the index subscript position in the new array e.next = newMap[index]; // The index position of the new array points to e newMap[index] = e; } // The result of the traversal is to put the elements in the linked list upside down into a new array } }
Six, remove ()
1. Source code
public synchronized V remove(Object key) { Entry tab[] = table; int hash = key.hashCode(); // locate the storage location int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { modCount++; // If the number of elements in the linked list is > 1, point the previous Entry.next to the next of the current element if (prev != null) { prev.next = e.next; } else { // traverse the conditions // Entry<K,V> e = tab[index], prev = null // In the next loop, assign prev = e; if prev == null // Explain that the first Entry in the linked list is the target tab[index] = e.next; } count--; // count represents the number of stored Entry V oldValue = e.value; e.value = null; return oldValue; } }
Seven, get ()
1. Source code
public synchronized V get(Object key) { Entry tab[] = table; // key cannot be null, a null pointer is reported here int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return e.value; } } return null; }