How HashMap 1.7 works

HashMap 

Hashmap essential data plus linked list. Obtain the hash value according to the key, and then calculate the subscript of the array. If multiple keys correspond to the same subscript, they are linked by a linked list, and the newly inserted one is in the front.

************************ One more point, the same hashCode of two Strings does not mean that the equals comparison will be equal, the difference between them is No necessary relationship ************************

Look at the 3 important code snippets:

    
    public HashMap(int initialCapacity, float loadFactor) {
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

There are 3 key parameters:
capacity: capacity, which is the size of the array
loadFactor: ratio, used for expansion
threshold:=capacity*loadFactor The maximum number of Entry that can be accommodated, if the current number of elements is more than this, the capacity will be expanded (capacity is expanded to the original 2 times)

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

Calculate the hash value according to the key, then obtain the array index according to the hash value, get the linked list through the array index, traverse the linked list and use equals to get the value of the corresponding key.

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

Get the linked list head from the array (by the hash value), then compare the keys by equals, if they are the same, overwrite the old value and return the old value. (The key already exists in the hashmap)

Otherwise, add an entry and return null. The newly added element is the head of the linked list, and the previous one at the same array position hangs at the back.

In addition: modCount is to avoid when a batch of data is read, if a modification occurs during the cyclic reading process, an exception is thrown

  if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
         

Let's look at adding a map element

    void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e) ;
        if (size++ >= threshold)
            resize(2 * table.length);
    }
After adding, if the size is found to be greater than the threshold, resize to twice the original size

    void resize(int newCapacity) {

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

Create a new array and transfer the original data to it

void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j ];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i ];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }
Take out the linked list in the original array one by one, then traverse each element in the linked list, recalculate the index and put it in the new table. array. Also put the linked list header for each process.

After taking out the original array list, empty the original array (for faster garbage collection when copying large amounts of data?)

Two more notes:

static class Entry<K,V> implements Map.Entry<K,V> is a static inner class of hashmap, and iterator is an inner class, because not every element needs to hold the this pointer of the map.

HashMap sets transient Entry[] table; and other variables to transient, then overrides readObject and writeObject, and implements serialization by itself.
 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325068121&siteId=291194637