Simulation of the whole process of adding data to android HashMap source code

Posted by onedumbcoder on Tue, 22 Feb 2022 07:45:58 +0100

The process of adding two pieces of data to hashmap is described earlier. When adding the first one, the data is empty, and the first judgment will be executed in putVal() method:

 if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

Will execute resize() to initialize the member variable table, whose length is threshold=16; If you continue to go down, you will trigger the second judgment:

 if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

Because n is the size of the tab, i.e. n=16, then let the hashcode of the key and 16-1 = 15 do the sum operation (if both are 1, it will be 1, otherwise it will be 0), and then get a value less than or equal to 15, that is, the subscript. The meaning of the judgment here can be understood as: according to the hashcode of the key passed in and the length of the table - 1, do the sum operation to get the subscript. If the tab [subscript] is empty, Then directly call the newNode () method to create a new linked list; Then judge the size of size + + and threshold. If it is less than, do not execute resize() and call afterNodeInsertion();

Here, the process of adding data is over; However, we will find that there is also an else judgment in the putVal () method (the code content is represented by three dots):

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            ...
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

What does that mean?

Explanation: when the value in tab [subscript] is empty, we execute newNode(), otherwise execute else. When the value in tab [subscript] is not! For! Empty!, It indicates that there is already an object in it. At this time, how do we need to save the current value into the linked list? As follows:

        else {
            Node<K,V> e; K k;
            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);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }

First judgment:

            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;

Here, let's sort out the required variables as follows:

The variables created in the first article of hashmap are: tab[11]=newNode(hash, key, value, null);hash=115864843,key="zhang",value=" ",

The new variables in the second part are: tab[13]=newNode(3261853,"jian","",null), that is:

tab=table=[ null, null, null, null, null, null, null, null, null, null, 11, null, 13 , null, null],

p=tab [subscript], we temporarily put the subscript = 11, that is: p=tab [11], k=p.key;

The subscripts are equal, or they come from the same object key="zhang", which is equivalent to the scene:

params.put("zhang","jian")
params.put("zhang","weijian")

Or it is a new object whose subscript is exactly equal to 11 after calculation. At this time, it comes from the same object, which is exactly the judgment:

     if (p.hash == hash &&
           ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;

Here is the assignment e=p;

Other judgments will not go away, but directly to:

            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }

In other words, when we put params twice, its value must be equal to the second time, that is:

params.put("zhang","jian")
params.put("zhang","weijian")


String value = params.get("zhang")

value Printing must be:"weijian";

Then look

         else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

That is, when the hash es are not equal or the contents of the key s are different, if p is a TreeNode object,

static final class TreeNode<K,V> extends LinkedHashMap.LinkedHashMapEntry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }

        ......
}

In terms of assignment, there is only one Node here, which is definitely not a TreeNode;

Continue to see the following else:

            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }

Here is the for loop, which assigns p.next to e. because p.next is equal to null, it will execute:

                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }

p.next=newNode(hash,key,value,null); Here is the newnode method, that is, another node is created and assigned to p.next. Continue to read:

                   if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;

Let's have a look:

    static final int TREEIFY_THRESHOLD = 8;

When bincount > = 8-1, that is, if the for loop has been executed for 7 times and is still executing, the treeifyBin() method will be executed. It should be a red black tree. We will only do it for the first time, so we won't execute it, and then go to break;

Here we know that when put is executed, when the calculated subscript of the added key has a value in the table, and their keys are not the same, there is just one node in the subscript, so the new node is directly the next of the above node, that is, the next of the original object, which is exactly equal to the new node;

Here, let's expand. If there are exactly two nodes in the subscript, then for the first time of the for loop, do not go to newNote, and then go to break,

So go on:

                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                    p = e;

Here, we add a new content. Therefore, it is not the same key. It will not break and will execute p=e;

Because at the beginning of the loop, e=p.next(), now e is assigned to P, which is equivalent to p=p.next. Here, the loop starts from the beginning. Let's judge the condition of for first, because we only have two nodes, so by the second loop, p.next() is null, so we will directly create a nowNote() assigned to P's next; At this point, the work of adding is completed;

So back to what we just discussed:

  • When put, after calculating the node, the value at the node is not empty. First traverse the node to the last next, that is, node If next = null, newNode() will be executed to create the node and assign the value to node next;

  • When there are 8 nodes in this node, the condition judgment of red black tree will be entered (we will analyze this in the next section)

  • When the same key is put multiple times, its key must be the value of the last put,

Topics: Java Android