HashTable principle and source code analysis

The copyright of this article belongs to feng lyh and blog park. Welcome to reprint it, but please keep this statement and give the original link. Tha...

The copyright of this article belongs to feng lyh and blog park. Welcome to reprint it, but please keep this statement and give the original link. Thank you for your cooperation. If there is any mistake, please don't hesitate to criticize and correct!

HashTable internal storage structure

The internal storage structure of HashTable stores data in the form of array + one-way linked list, i.e. the defined entry <?,? > [] table variable

Source code analysis:

Variable definition:

//Use Entry Array storage data (Entry One-way linked list) private transient Entry<?,?>[] table; //Already stored in table Of Entry Number private transient int count; /**** * Entry When the array expansion threshold (count) count > = threshold, the expansion Entry array will be expanded * It is recommended not to set more than 1 or set too large, which will lead to too long linked list and slow query * For example, if hashtble initialCapacity = 5 LoadFactor = 0.75, then calculate threshold =3 * when count >=3 When table starts to expand (how to expand depends on the expansion code) * ***/ private int threshold; //Load factor for calculation threshold private float loadFactor; //Record modification times private transient int modCount = 0; //table Maximum length of array private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

Entry point linked list structure

/** * Hashtable bucket collision list entry *One-way linked list */ private static class Entry<K,V> implements Map.Entry<K,V> { //hash value final int hash; //key final K key; //value V value; //Successor 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(); } }

Constructor

/*** * Initialize HashTable specifies initial capacity and load factor * HashTable Initialize core code * **/ 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; //Initialization Entry array table = new Entry<?,?>[initialCapacity]; //Calculate expansion threshold threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); } /*** * Initialize HashTable specifies the initial capacity * The default load factor size is 0.75 * ***/ public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } /*** * The default initialization Entry array size is 11 * loadFactor =0.75 ***/ 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); }

put method

/*** **Add element key value ** Note: put method is locked, which is why hashTable is blocked due to thread safety ****/ 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; //hashCode What is taken is key Of HashCode int hash = key.hashCode(); //according to hashCode & long The maximum hash gets the index of the array to be inserted by taking the modulus of the array length int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") //Check whether there are elements under the subscript of the array. If there is convenience value(new)cover key Same value 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; } } //Preservation addEntry(hash, key, value, index); return null; } /*** *Preservation ***/ private void addEntry(int hash, K key, V value, int index) { //Record put frequency +1 modCount++; Entry<?,?> tab[] = table; //Stored Entry Number >= Threshold capacity expansion 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") //Query the original Entry Linked list Entry<K,V> e = (Entry<K,V>) tab[index]; //take Entry<K,V> Save and Entry<K,V>.next Point to the original Entry Linked list tab[index] = new Entry<>(hash, key, value, e); //Array length+1 count++; }

Capacity expansion mechanism

/**** **Capacity expansion ** Trigger count > = threshold ****/ protected void rehash() { //Array before expansion(table)length int oldCapacity = table.length; //Before expansion table array Entry<?,?>[] oldMap = table; // overflow-conscious code //The expanded array length is oldCapacity*2+1 int newCapacity = (oldCapacity << 1) + 1; //Check if the length exceeds the upper limit if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } // Create a new array with a length of oldCapacity*2+1 array Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount++; //Computing threshold threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; //takeIn the old array Entry Recalculate transfer to new array 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; } } }

Characteristic:

    1. key and value are not allowed to be empty. key is not allowed to be empty because hashcode takes the HashCode()
    2. The default construction method capacity of HashTable is initialized to 11 with a load factor of 0.75. If you use a construction method with a load factor to create a HashTable, please don't say that the load factor setting is too large. For example, if the initialization capacity is set to 1 and the load factor is set to 1000, the query will be slow
    3. HashTable is an array + single linked list entry < K, V > structure to store data
    4. The maximum length of array table is Integer.Max - 8;
    5. Capacity expansion condition count (number of hashtbale storage chain tables) > = threshold (threshold calculation method table.length * loadFactor)
    6. Thread safety

3 December 2019, 04:44 | Views: 2923

Add new comment

For adding a comment, please log in
or create account

0 comments