Analysis of HashMap underlying source code -- implementation principle of put

Today, I taught myself the source code implementation of HashMap put to record that I may write disorderly when I understand HashMap for the first time. If there is anything wrong, let's comment and discuss it together

1. Important constants in the source code

DEFAULT_ INITIAL_ Capability: default capacity of HashMap, 16

/**
* The default initial capacity - MUST be a power of two.
* Default initial capacity - must be a power of 2.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

DEFAULT_LOAD_FACTOR: default load factor of HashMap

/**
 * The load factor used when none specified in constructor.
 * The load factor to use when not specified in the constructor
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

table: an array of storage elements, always to the nth power of 2

transient Node<K,V>[] table;

TREEIFY_THRESHOLD: if the length of the linked list in the Bucket is greater than the default value, it will be converted into a red black tree

static final int TREEIFY_THRESHOLD = 8;

MIN_ TREEIFY_ Capability: the minimum hash table capacity when the nodes in the bucket are trealized.

(when the number of nodes in the bucket is so large that the tree needs to be reddened and black, if the hash table capacity is less than min_tree_capability, the resize expansion operation should be performed at this time. The value of min_tree_capability is at least 4 times that of tree_threshold.)

static final int MIN_TREEIFY_CAPACITY = 64;

2. Put method

put method. When putting for the first time, enter the resize() method to create an array

Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

resize method

else {               // zero initial threshold signifies using defaults
    newCap = DEFAULT_INITIAL_CAPACITY; //Default capacity of HashMap, 16
    newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//Calculate the critical value of capacity expansion 12
}
...
threshold = newThr;//The critical value of capacity expansion is assigned to threshold. The critical value of capacity expansion = HashMap default capacity * default load factor
...
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];	//Create a Node [] array with a length of 16
table = newTab;//Assign an array to table

Judgment steps

  1. First, call hashCode() of the class where key1 is located to calculate the hash value of key1. After some algorithm, the hash value will be stored in the Node array
  2. If the data in this location is empty, key1-value1 is added successfully
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
  1. If the data in this location is not empty, (which means that there are one or more data in this location (in the form of linked list)), compare the hash values of key1 and one or more existing data
  • If the hash value of Key1 is different from that of existing data, key1-value1 is added successfully
  • If the hash value of key1 is the same as the hash value of an existing data (key2-value2), continue the comparison: call equals(key2) of the class where key1 is located
    • If equals() returns false: key1-value1 is added successfully
    • If equals() returns true: replace value2 with value1
else {
    Node<K,V> e; K k;
    
    //Compare the hash value and the equals() method. If they are equal, it indicates that the key exists. Assign it to e, and then replace it
    if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
    
    else if (p instanceof TreeNode)
        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    
    //The equals() method does not hold when comparing hash values
    else {
        //Traverse the linked list that may exist at the current location
        for (int binCount = 0; ; ++binCount) {
            
            //If there is no linked list in the current location, add it directly to p's next
            if ((e = p.next) == null) {
                p.next = newNode(hash, key, value, null);
                
                //If the traversal subscript is greater than or equal to 7 (that is, the length of the linked list is greater than 8, and the ninth node is inserted), enter the treeifyBin method
                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    treeifyBin(tab, hash);
                break;
            }
            
            //If there is a linked list, continue to compare the hash value and the equals() method
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                break;
            
            //And re assign p.next to P and traverse the next element of the linked list again
            p = e;
        }
    }
    
    //As long as the key exists, this method will be used to replace the value
    if (e != null) { // existing mapping for key
        V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null)
            e.value = value;
        afterNodeAccess(e);
        return oldValue;
    }
}
  1. When the number of data in the form of a linked list of elements at an index position of the array is > 8 and the length of the current array is > 64, all data at this index position is stored in a red black tree

    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        
        //If the hash table is empty or the hash table length is less than 64, expand the capacity without converting the red black tree
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        
        //If the number of data in the form of linked list is > 8 and the length of the current array is > 64, it is converted to a red black tree
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }
    

Tags: Java

Posted on Sat, 18 Sep 2021 12:53:03 -0400 by Emir