First look at the important attributes in the HashMap:

static final int DEFAULT_INITIAL_CAPACITY = 16// The default length of the main array is 16

static final float DEFAULT_LOAD_FACTOR = 0.75f; // Default load factor is 0.75

transient Entry<K,V>[] table; // Main array. Each element is of type Entry

transient int size; // The number of elements in the interface defaults to 0

int threshold;// The threshold value is 16*0.75=12

final float loadFactor;// The variable used to receive the load factor
Copy the code

Look at the hashMap no-parameter constructor

public HashMap() {

        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);  / / this (16,0.75 f)

    }
Copy the code

Hashmap parameter constructor: initializes some values

public HashMap(int initialCapacity, float loadFactor) {

        Capacity must be greater than the minimum NTH power of 2 of initialCapacity that you passed in
        int capacity = 1;

        while (capacity < initialCapacity)   // The while loop breaks. Capacity is 16
            capacity <<= 1;
                // To assign loadFactor, assign the loadFactor 0.75 to loadFactor
                this.loadFactor = loadFactor;
                // The threshold of array expansion is 12
                threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
                // Assign to the table array, initialize the array length to 16

        table = new Entry[capacity];

    }
Copy the code

The put method:


public V put(K key, V value) {

        // The hashMap key can be null
        if (key == null)
            return putForNullKey(value);
            // Call the hash method to get the hash code
            int hash = hash(key);
            // Get the position of the key in the array
            int i = indexFor(hash, table.length);
            // If the element you put in has no value at that position in the main array, e==null then the following loop does not work
            // When an element is placed in the same place, e! = null indicates that there are already elements (that is, hash collisions)
            for(Entry<K,V> e = table[i]; e ! =null; e = e.next) {
            Object k;
            // the hash value is the same (k = e.key) == key
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                // Get the old value
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                / / return oldValue
                return oldValue;
            }
        }

        modCount++;

         // go to addEntry to add this node:
        addEntry(hash, key, value, i);
        return null;
    }
Copy the code

// The hash method returns the corresponding hash value of the key.

final int hash(Object k) {

        int h = 0;
        
        if (useAltHashing) {
            if (k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }
            h = hashSeed;
        }

      // k.hashcode () calls the hash function of the key type,

      // Since different objects may have the same hashCode(), hashCode() needs to be hashed again to reduce the sameness rate.

        h ^= k.hashCode();

                /* The next series of and operations and xOR operations are called "perturbation function". The core idea of perturbation is to increase the uncertainty of the calculated value while retaining the original related characteristics, so as to reduce the probability of conflict. Different versions implement it differently, but the underlying idea is the same. The purpose of moving to the right is to take advantage of the high position of H and reduce kazakh conflict */

        h ^= (h >>> 20) ^ (h >>> 12);

        return h ^ (h >>> 7) ^ (h >>> 4);

    }
Copy the code

// Return the coordinates of an array of type int (get the position of the key in the corresponding array)

        static int indexFor(int h, int length) {

        // This algorithm is the same as h%length
        return h & (length-1);
    }
Copy the code

/ / call addEntry

void addEntry(int hash, K key, V value, int bucketIndex) {

        // if the size is greater than 16*0.75=12, for example, if you want to place the 13th element where there is no element
        if ((size >= threshold) && (null! = table[bucketIndex])) {// The main array is doubled
            resize(2 * table.length);
            // Rehash the hash code of the current element
            hash = (null! = key) ? hash(key) :0;
            // Recalculate the element position
            bucketIndex = indexFor(hash, table.length);
        }

        / / the hash key, value, bucketIndex position Packaging for an Entry object:
        createEntry(hash, key, value, bucketIndex);
    }

        void createEntry(int hash, K key, V value, int bucketIndex) {

        // Get the bucketIndex element to e
        Entry<K,V> e = table[bucketIndex];

        // Wrap hash, key, and value as an object, and set the next element to e.
        // Place the new Entry in table[bucketIndex]
        table[bucketIndex] = new Entry<>(hash, key, value, e);

        // Add an element size+1 to the set
        size++;

    }
Copy the code

Summary of basic working principles of HashMap (1.7) :

In a Hashmap, the underlying main array is an Entry[] array, with a length of 16 and a default load factor of 0.75 (if the loading factor is 1, then the array is full and then expanded to achieve maximum space utilization). However, this is an ideal state, since elements cannot be completely evenly distributed, it is likely that hash collisions will generate linked lists. It takes a long time to generate linked lists. (If it is 0.5, it will waste space, but if it can be expanded to 0.5, then hashi collisions will be less, and no linked list will be generated. Therefore, the median value between space and time is 0.75, The limit value of array expansion is 16 x 0.75 = 12. During the PUT method, the hash code is first obtained through the hash method (The hash method returns the hash value corresponding to the key, which is internally hashed twice to ensure that different keys get different hash codes. Moreover, different objects may have the same Hashcode, so the hashcode needs to be hashed again to reduce the sameness rate, and internally uses a string of and and xOR operations (also known as the perturbation function, To obtain the position of the key in the corresponding array (internally achieved by bit operation), then add elements to the set, see the above for details of the process of adding elements (there are detailed instructions)!

Why is the length of the main array a multiple of 2?

The length of the key will affect the position of the key.

If length is not an integer multiple of 2, the probability of hash collisions is much higher.

An integer multiple of 2:

If not an integer multiple of 2:

If it is not an integer multiple of 2, then the probability of hash collision is greatly increased