HashMap source shallow in deep out (2)

Short Book Pay Cat
Please note the origin of the original, thank you!
This series focuses on HashMap(1.8), records are also shared, welcome to discuss

When we talked about hatred and love of the get method in the last book, we mainly introduced the basic structure of HashMap and how HashMap's hash values are calculated. These can lay a foundation for our put method. Share the put method today.

2.put(K,V)

As always, start with the source code

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

put method is finished, let's talk about putVal. (This cat series will focus on JDK1.8. If you find your source may be a fake source when someone looks at it, please change the JDK version, hash() method can be seen in the last section)

putVal(int hash,K key,V value, boolean onlyIfAbsent,boolean evict)

First, several parameters of this method are introduced:
Hash is the hash value that was handled by HashMap as we said in the previous section
Key and value aside, the onlyIfAbsent parameter is used to determine whether we will replace the key if it already exists, true: no replacement, false: replacement.
The parameter evict is used for LinkedHashMap, let's not talk about it for a moment, but let's take a closer look when we share LinkedHashMap later
Let's start with putVal's code

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 {//④
            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;
            }
        }
        ++modCount;//⑫
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

The above code is very long, and the phone does not appear to be highlighted, which makes it even more difficult to see.So readers can use the above as an institution, and I'll break it down below.The splitting below works even better with the notes above.

 Node<K,V>[] tab; Node<K,V> p; int n, i;

When you first look at the source code, it's easy to confuse variables, especially when you look in the middle and suddenly think, Ouch?Who am I, where am I, and what am I doing.So the best way is to look at the code and add your own comment when you understand what this variable means.
Array of Node nodes stored in tab:HashMap
p: The key currently being put into HashMap, the calculated tab subscript corresponds to the first Node.(What?What am I saying?The first Node means that our Node is essentially a node in the list, and we can only find the head of that list according to Key) This is more precisely the node that is currently being processed
Current size of n:tab
i:key calculated subscript

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

The table is a global variable, set tab= table, n = tab.length first, and if you decide that the tab is null or that the tab size is 0, we will call the resize() method to do the first expansion (resize() method. As detailed below, you can temporarily interpret this as initializing HashMap. What we know from this is that HashMap is not generated when instantiating the conversationNode <K, V>[] of the specified size is not initialized until the first put.

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

Here is how we calculate the subscript based on the calculated hash value (see the previous section for the specific calculation method), (n-1) & hash, (15&3=3, 15&18=2, not to mention hash~ with the operation here. If the calculated subscript i, tab[i] is empty, we can just insert a new Node under tab[i].

(4) It seems like a big block, which is also the key point of understanding this function. You can look at the code below, of course I will also label it above
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;
            }
        }

This section focuses on how HashMap handles when the computed index subscript already has a value.

Node<K,V> e; K k;

E: If the key to put into the HashMap already exists, e is the node of the key, or null if it does not exist.
k:The key of the current node when traversing the list of chains

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

Always judge the first, if the hash values calculated are the same and the keys are the same (the same key means either the same object or equals returns true), then e=p goes directly to the judgment of_

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

If p is already a TreeNode class (that is, it has been converted to a red-black tree), call putTreeVal of the red-black tree directly. We will talk more specifically after this method, as long as we know that if key does not exist, this method returns null, and if there is a judgment that returns the corresponding node to enter_

for (int binCount = 0; ; ++binCount) {
.....
}

This loop is primarily used to record the number of linked lists, which will then be converted to a red-black tree if the default value (8) is exceeded.

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==null means that we have found the last node by traversing the list, that is, although we have calculated that there is a chain table under the subscript, but there is no same key, so directly creating a new node is placed at the end of the list.At the same time, if the length of this list is longer than the default value of 8, the current list will be converted to a red-black tree, specific treeifyBin() method we will share when we share the red-black tree later.

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

If the key to put has been found, go directly to_judgment

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

In general, our put methods will directly override the old values. In fact, what we wanted to say at the beginning is that the put method has a return value!If an old value returns an old value, null is returned if there is no old value.afterNodeAccess This method is used by LinkedHashMap and is not covered here

  ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;

First the code will come here, either 3 is true or_is false. What do these two mean? That means there was no same key before, that is, the size of HashMap will be + 1, ++ modCount won't mention it for the moment. Let's look at the next one, if size+1 is later than threshold (the value of this variable will be re-introduced here when we talk about resize you can simply assume it isCapacity * 0.75), will expand capacity.
The afterNodeInsertion() method is also LinkedHashMap's method, which is not described here. The Put method is now complete ~~

The cat's expectation is to make it easier for everyone to learn the source code and understand the source code. If you have any suggestions, leave a message below. ~We will talk about the resize() method next time.

I'm a paid cat
Technology is a prerequisite for a raise, and life is what it means
Technology equals life

Keywords: JDK

Added by amclean on Sun, 26 May 2019 20:44:03 +0300