Analysis of the whole process of adding data to the android HashMap source code (1)

        String name="zhang";
        Log.e(TAG, "initView name: "+name.hashCode() );
        HashMap<String, String> params = new HashMap<>();
        params.put(name,"");

The name here, the printed value is: 115864556

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


    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

Here, we ask what is the value returned by hash (Object key), namely:

key.hashcode() =115864556

In the hash(Object key) method, the key is not equal to null, so return: 115864556 ^ (115864556 >>> 16)

115864556 ^ (115864556 >>> 16) is decimal, we convert it to binary

which is:

110111001111111001111101100 ^ (110111001111111001111101100 >>> 16)

>>> means positive shift to the right, that is, if it is a negative right shift, then its result is positive,

110111001111111001111101100 is shifted to the right by 16 bits, counted 16 from right to left, and the numbers on the right are removed, that is: 11011100111, there are no high bits, we add 0, that is: 000000000000000011011100111

so:

110111001111111001111101100 ^ (110111001111111001111101100 >>> 16)

= 110111001111111001111101100 ^ 000000000000000011011100111

XOR operation is 2 if they are the same, 0, and 1 if they are different

110111001111111001111101100

000000000000000011011100111

They are too densely spaced, so add a small space to make it easier to calculate, namely:

11 011 100 111 111 1001 111 101 100

00 000 000 000 000 0011 011 100 111

11 011 100 111 111 1010 100 001 011 Its decimal is: 115864843

Then what is returned here is: 115864843

Then follow the method putVal(), we first correspond to the parameters:

hash:115864843,        key:"zhang",        value:"",        onlyIfAbsent:false,        evict:true

 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;
    }

Just come in, the table must be null, this condition will be executed

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

see resize() method

 final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

Because the first time you enter, you will reach the conditions:

            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

Equivalent to initializing the table, newCap is equal to threshold=16, which is the default size;

So n=tab.length=16

Then the second judgment in the putVal method:

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

n=16,p=null,i=0,hash=115864843即:

(p=tab[ i= (n-1) & hash])==null) 即:(null=tab[i=16-1&115864843])==null)

即:(null=tab[15&115864843]==null)

(The readability is extremely poor; although the readability is poor here, the newly created local variables are reduced, and the put is definitely used frequently, so the number of new local variables is reduced, and the memory will not be newly created at a time, but recycled at a time, to avoid frequent Memory new variables, recycling variables)

Calculation and operation: 15&115864843 (and operation, if 2 is 1, then 1)

1111&110111001111111010100001011

0000 0000 0000 0000 0000 0001 111

1101 1100 1111 1110 1010 0001 011

0000 0000 0000 0000 0000 0001 011 Its decimal is: 11;

Good guy, a magical scene has appeared, where is the magic? 1. The hashcode value of the object can be a very large number, and then XOR with a number of threshold-1, the result is a very small number, this number is in the range of threshold-1, including threshold- 1 itself, so this algorithm is used to calculate the subscript value; no addition, subtraction, multiplication and division are used, only bit operations are used, and the performance is the highest; the threshold is animated, its value is 2 to the nth power, and the default is 16 , that is, 2 to the 4th power 2*2*2*2=16, 2*2*2*2*2=16*2=32, 2*2*2*2*2*2=32*2=64 ,

So the judgment here is: ((null=tab[11])==null), tab only has length and space, but the value inside has not been added yet, it is empty, so the code below it will be executed :

 tab[i] = newNode(hash, key, value, null);

Here it means tab[11]=newNode(hash, key, value, null);

which is:

    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next);
    }

Here is to return a new Node<>(), which is to create a new object node

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

Seeing this, don't read it in our else, just go directly to it

        else{...}
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;

The size++ here is 0+1=1, which is less than the default length of 16, so the resize() method is not executed; continue to execute afterNodeInsertion(evict); evict is true, the parameters passed above;

  •         Here's a summary:

1. When put, the hashMap will perform an XOR operation with the value of the object's hashcode of the key and the value of itself shifted to the right by 16 bits (the two are the same as 0, otherwise it is 1),

2. Just came in, there is no value in the table array, it is null, the table value will be initialized, and the size is the default size of 16,

3. When inserting, it is necessary to determine the subscript value. Here, the value of the XOR operation and the value of the threshold are used for & and operation (if the two are 1, it is 1, otherwise it is 0). The subscript value here must be Less than or equal to the value of threshold, its value is the nth power of 2, and the performance is higher than ordinary addition, subtraction, multiplication and division; here, if table [subscript value] is empty, a new node is created directly;

4. Finally, the size will be ++, if the size is less than or equal to the threshold, the size will not be adjusted;

After the analysis, everyone has digested and digested it. After the digestion is complete, we move to the next section and continue to analyze and discuss:

The whole process simulation of adding data to the android HashMap source code (2) - Programmer Sought

Guess you like

Origin blog.csdn.net/jian11058/article/details/123047491