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. 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

Tags: Java

Posted on Tue, 03 Dec 2019 04:44:10 -0500 by Zaid