JAVA HashMap details and examples

  • 2020-04-01 02:29:12
  • OfStack

Part 1 introduces HashMap
Introduction of a HashMap
A HashMap is a hash table that stores key-value mappings.
HashMap is subclassed from AbstractMap and implements the Map, Cloneable, java.io.Serializable interfaces.
The implementation of HashMap is not synchronous, which means it is not thread-safe. Its key and value can be null. Furthermore, the maps in a HashMap are not ordered.
An instance of a HashMap has two parameters that affect its performance: "initial capacity" and "load factor." The capacity is the number of buckets in the hash table, and the initial capacity is just the capacity of the hash table when it was created. A load factor is a scale at which a hash table can reach multi-full before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, the internal data structure is reconstructed) so that the hash table will have approximately twice the number of buckets.
Typically, the default load factor is 0.75, which seeks a compromise in terms of time and space costs. While the high load factor reduces the space overhead, it also increases the query cost (as reflected in the operations of most HashMap classes, including the get and put operations). The number of entries required in the map and their loading factors should be taken into account when setting the initial capacity to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, the rehash operation does not occur.

Inheritance of HashMap
< img SRC = "border = 0 / / files.jb51.net/file_images/article/201311/20131105110522.jpg? 2013105112029 ">
The relation between HashMap and Map is as follows:
< img SRC = "border = 0 / / files.jb51.net/file_images/article/201311/20131105110546.jpg? 2013105112059 ">
The constructor for HashMap
There are four constructors of HashMap, as follows:


//Default constructor.
HashMap()
//Specifies the constructor for capacity size
HashMap(int capacity)
//Specifies the constructor for capacity size and load factor
HashMap(int capacity, float loadFactor)
//Contains the constructor for the submap
HashMap(Map<? extends K, ? extends V> map)

The HashMap API

void                 clear()
Object               clone()
boolean              containsKey(Object key)
boolean              containsValue(Object value)
Set<Entry<K, V>>     entrySet()
V                    get(Object key)
boolean              isEmpty()
Set<K>               keySet()
V                    put(K key, V value)
void                 putAll(Map<? extends K, ? extends V> map)
V                    remove(Object key)
int                  size()
Collection<V>        values()


Part 2: HashMap source code parsing
To better understand the principles of HashMap, the following is an analysis of the HashMap source code.
As you read the source code, it is recommended that you refer to the following instructions to build an overall understanding of the HashMap so that it is easier to understand.

package java.util;
import java.io.*;
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{
    //The default starting capacity is 16, which must be a power of 2.
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    //Maximum capacity (must be a power of 2 and less than 2 to the 30th, too much incoming capacity will be replaced by this value)
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //Default loading factor
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //The Entry array to store the data is a power of 2.
    //HashMap is implemented using the zipper method, and each Entry is essentially a one-way linked list
    transient Entry[] table;
    //The size of a HashMap, which is the number of key-value pairs that a HashMap holds
    transient int size;
    //Threshold of a HashMap to determine whether the capacity of a HashMap needs to be adjusted (threshold = capacity * load factor)
    int threshold;
    //Actual load factor size
    final float loadFactor;
    //The number of times the HashMap was changed
    transient volatile int modCount;
    //Specifies the constructor for capacity size and load factor
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        //The maximum capacity of a HashMap can only be MAXIMUM_CAPACITY
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        //Find the smallest power of two greater than initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        //Set the "load factor"
        this.loadFactor = loadFactor;
        //Set the "HashMap threshold", and when the amount of stored data in the HashMap reaches the threshold, the capacity of the HashMap needs to be doubled.
        threshold = (int)(capacity * loadFactor);
        //Create an Entry array to hold the data
        table = new Entry[capacity];
        init();
    }
    //Specifies the constructor for capacity size
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    //Default constructor.
    public HashMap() {
        //Set the "load factor"
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        //Set the "HashMap threshold", and when the amount of stored data in the HashMap reaches the threshold, the capacity of the HashMap needs to be doubled.
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        //Create an Entry array to hold the data
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }
    //Contains the constructor for the submap
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        //Add all the elements in m to the HashMap one by one
        putAllForCreate(m);
    }
    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
    //Return index value
    //H & (length-1) ensures that the return value is less than length
    static int indexFor(int h, int length) {
        return h & (length-1);
    }
    public int size() {
        return size;
    }
    public boolean isEmpty() {
        return size == 0;
    }
    //Gets the value corresponding to the key
    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        //Gets the hash value of the key
        int hash = hash(key.hashCode());
        //Look up the key equals key element on the linked list for this hash value
        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;
    }
    //Gets the value of the "null key" element
    //HashMap stores the "null key" element at table[0]!
    private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
    //Whether the HashMap contains the key
    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }
    //Returns the key-value pair for key
    final Entry<K,V> getEntry(Object key) {
        //Gets the hash value
        //A HashMap stores "null key" elements in the table[0] location, and "non-null key" calls hash() to calculate the hash value
        int hash = (key == null) ? 0 : hash(key.hashCode());
        //Look up the key equals key element on the linked list for this hash value
        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 != null && key.equals(k))))
                return e;
        }
        return null;
    }
    //Add "key-value" to the HashMap
    public V put(K key, V value) {
        //If "key is null", the key-value pair is added to table[0].
        if (key == null)
            return putForNullKey(value);
        //If "key is not null," calculate the hash value of the key, and then add it to the linked list corresponding to the hash 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 the key-value pair corresponding to "the key" already exists, replace the old value with the new value. And quit!
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        //If the key-value pair corresponding to "the key" does not exist, add "key-value" to the table
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
    //PutForNullKey () adds the "key is null" key-value pair to the table[0] position
    private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        //This is not going to be executed at all!
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }
    //Create the "add method" for HashMap,
    //It's different from put(). PutForCreate () is an internal method that is called by constructors and others to create a HashMap
    //Put () is the externally provided method for adding elements to a HashMap.
    private void putForCreate(K key, V value) {
        int hash = (key == null) ? 0 : hash(key.hashCode());
        int i = indexFor(hash, table.length);
        //If there is an element in the HashMap table whose key value is equal to key, the value value of that element is replaced
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                e.value = value;
                return;
            }
        }
        //If there is no "key-value equal to key" element in the HashMap table, the key-value is added to the HashMap
        createEntry(hash, key, value, i);
    }
    //Add all the elements in "m" to the HashMap.
    //This method is called by the internal method that constructs the HashMap.
    private void putAllForCreate(Map<? extends K, ? extends V> m) {
        //The iterator is used to add the elements one by one to the HashMap
        for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            putForCreate(e.getKey(), e.getValue());
        }
    }
    //Resize the HashMap, and newCapacity is the adjusted unit
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        //Create a new HashMap, add all the elements of the old HashMap to the new HashMap,
        //Then, assign the new HashMap to the old HashMap.
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }
    //Add all the elements in the HashMap to the newTable
    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);
            }
        }
    }
    //Add all the elements of "m" to the HashMap
    public void putAll(Map<? extends K, ? extends V> m) {
        //Validity judgment
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;
        //Whether the calculation capacity is sufficient,
        //If "current actual capacity <The required capacity ", then the capacity x2.
        if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }
        //Through the iterator, the elements in "m" are added to the HashMap one by one.
        for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            put(e.getKey(), e.getValue());
        }
    }
    //Delete the key as key element
    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }
    //Delete the "key as key" element
    final Entry<K,V> removeEntryForKey(Object key) {
        //Gets the hash value . if key for null , then the hash value is 0 ; Otherwise the call hash() To calculate 
        int hash = (key == null) ? 0 : hash(key.hashCode());
        int i = indexFor(hash, table.length);
        Entry<K,V> prev = table[i];
        Entry<K,V> e = prev;
        //Deletes "key" elements from a linked list
        //Essentially, "delete a node from a one-way linked list."
        while (e != null) {
            Entry<K,V> next = e.next;
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }
        return e;
    }
    //Delete key value pair
    final Entry<K,V> removeMapping(Object o) {
        if (!(o instanceof Map.Entry))
            return null;
        Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
        Object key = entry.getKey();
        int hash = (key == null) ? 0 : hash(key.hashCode());
        int i = indexFor(hash, table.length);
        Entry<K,V> prev = table[i];
        Entry<K,V> e = prev;
        //Delete the "key value pair e" in the linked list
        //Essentially, "delete a node from a one-way linked list."
        while (e != null) {
            Entry<K,V> next = e.next;
            if (e.hash == hash && e.equals(entry)) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }
        return e;
    }
    //Empty the HashMap and set all the elements to null
    public void clear() {
        modCount++;
        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            tab[i] = null;
        size = 0;
    }
    //Whether it contains an element with a value
    public boolean containsValue(Object value) {
    //If "value is null", then containsNullValue() is called for lookup
    if (value == null)
            return containsNullValue();
    //If "value is not null," look for any nodes with value in the HashMap.
    Entry[] tab = table;
        for (int i = 0; i < tab.length ; i++)
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (value.equals(e.value))
                    return true;
    return false;
    }
    //Contains a null value
    private boolean containsNullValue() {
    Entry[] tab = table;
        for (int i = 0; i < tab.length ; i++)
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (e.value == null)
                    return true;
    return false;
    }
    //Clone a HashMap and return an Object
    public Object clone() {
        HashMap<K,V> result = null;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // assert false;
        }
        result.table = new Entry[table.length];
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        //Call putAllForCreate() to add all the elements to the HashMap
        result.putAllForCreate(this);
        return result;
    }
    //Entry is a one-way linked list.
    //It is the linked list of "HashMap chained storage".
    //It implements the map.entry interface, which implements the functions getKey(), getValue(), setValue(V value), equals(Object o), and hashCode()
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        //Point to the next node
        Entry<K,V> next;
        final int hash;
        //Constructor.
        //Input parameters include "hash value (h)"," key (k)", "value (v)"," next node (n)"
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        public final K getKey() {
            return key;
        }
        public final V getValue() {
            return value;
        }
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        //Determine if the two entries are equal
        //Returns true if the "key" and "value" of both entries are equal.
        //Otherwise, false is returned
        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }
        //Implement hashCode ()
        public final int hashCode() {
            return (key==null   ? 0 : key.hashCode()) ^
                   (value==null ? 0 : value.hashCode());
        }
        public final String toString() {
            return getKey() + "=" + getValue();
        }
        //When an element is added to a HashMap, it calls recordAccess().
        //I'm not doing anything here
        void recordAccess(HashMap<K,V> m) {
        }
        //RecordRemoval () is called when an element is removed from a HashMap.
        //I'm not doing anything here
        void recordRemoval(HashMap<K,V> m) {
        }
    }
    //New Entry. Insert "key-value" into the specified location. BucketIndex is the location index.
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //Save the value of the "bucketIndex" position to "e"
        Entry<K,V> e = table[bucketIndex];
        //Set the element in the "bucketIndex" position to "new Entry",
        //Set "e" to "next node of new Entry"
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        //If the actual size of the HashMap is no less than the threshold, adjust the size of the HashMap
        if (size++ >= threshold)
            resize(2 * table.length);
    }
    //Create the Entry. Insert "key-value" into the specified location. BucketIndex is the location index.
    //The difference between it and addEntry is:
    //(01) addEntry() is generally used when adding an Entry might cause the actual capacity of a "HashMap" to exceed the "threshold."
    //    For example, we create a new HashMap, and then we constantly add elements to the HashMap by putting ().
    //Put () is added through addEntry().
    //    In this case, we don't know when the actual capacity of the HashMap will exceed the threshold;
    //    Therefore, you need to call addEntry()
    //(02) createEntry() is generally used when adding an Entry does not cause the actual capacity of a HashMap to exceed the threshold.
    //    For example, we call the constructor of HashMap "with Map", which adds all the elements of the Map to the HashMap.
    //But before we add, we have calculated the capacity and threshold of the HashMap. That is, it can be determined "even if the Map will be in
    //All of the elements are added to the HashMap and do not exceed the HashMap threshold.
    //    At this point, call createEntry().
    void createEntry(int hash, K key, V value, int bucketIndex) {
        //Save the value of the "bucketIndex" position to "e"
        Entry<K,V> e = table[bucketIndex];
        //Set the element in the "bucketIndex" position to "new Entry",
        //Set "e" to "next node of new Entry"
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        size++;
    }
    //HashIterator is an abstract parent class of a HashMap iterator that implements a common function.
    //It contains three subclasses: "key iterator", "Value iterator", and "Entry iterator".
    private abstract class HashIterator<E> implements Iterator<E> {
        //Next element
        Entry<K,V> next;
        //ExpectedModCount is used to implement the fast-fail mechanism.
        int expectedModCount;
        //The current index
        int index;
        //The current element
        Entry<K,V> current;
        HashIterator() {
            expectedModCount = modCount;
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                //Point next to the first element in the table that is not null.
                //Here, the initial value of index is used to be 0, and the loop is iterated back from 0 until an element that is not null is found.
                while (index < t.length && (next = t[index++]) == null)

            }
        }
        public final boolean hasNext() {
            return next != null;
        }
        //Gets the next element
        final Entry<K,V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            //Attention!!
            //An Entry is a one-way linked list
            //If the next node of the Entry is not empty, point next to the next node;
            //Otherwise, point next to the non-null node of the next list (also the next Entry).
            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)

            }
            current = e;
            return e;
        }
        //Delete the current element
        public void remove() {
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }
    //Iterator for value
    private final class ValueIterator extends HashIterator<V> {
        public V next() {
            return nextEntry().value;
        }
    }
    //Key iterator
    private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
    //Entry's iterator
    private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }
    //Return a "key iterator"
    Iterator<K> newKeyIterator()   {
        return new KeyIterator();
    }
    //Return a "value iterator"
    Iterator<V> newValueIterator()   {
        return new ValueIterator();
    }
    //Return an "entry iterator"
    Iterator<Map.Entry<K,V>> newEntryIterator()   {
        return new EntryIterator();
    }
    //The collection corresponding to the Entry of the HashMap
    private transient Set<Map.Entry<K,V>> entrySet = null;
    //Returns a "collection of keys," which actually returns a "KeySet object."
    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }
    //The set of keys
    //KeySet is subclassed from AbstractSet, indicating that there are no duplicate keys in the collection.
    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }
    //Returns the "value collection", which actually returns a Values object
    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }
    //"The value set"
    //Values subclass from AbstractCollection, unlike "KeySet subclass from AbstractSet",
    //The elements in Values can be repeated. Because different keys can point to the same value.
    private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return newValueIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            HashMap.this.clear();
        }
    }
    //Return "Entry collection of HashMap"
    public Set<Map.Entry<K,V>> entrySet() {
        return entrySet0();
    }
    //Return "Entry collection of HashMap" It actually returns one EntrySet object 
    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }
    //The corresponding set of EntrySet
    //An EntrySet is subclassed from AbstractSet, indicating that there is no duplicate EntrySet in the collection.
    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
            Entry<K,V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }
        public int size() {
            return size;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }
    //Write to java.io.Serializable
    //Write the "total capacity, actual capacity, all entries" of the HashMap to the output stream
    private void writeObject(java.io.ObjectOutputStream s)
        throws IOException
    {
        Iterator<Map.Entry<K,V>> i =
            (size > 0) ? entrySet0().iterator() : null;
        // Write out the threshold, loadfactor, and any hidden stuff
        s.defaultWriteObject();
        // Write out number of buckets
        s.writeInt(table.length);
        // Write out size (number of Mappings)
        s.writeInt(size);
        // Write out keys and values (alternating)
        if (i != null) {
            while (i.hasNext()) {
            Map.Entry<K,V> e = i.next();
            s.writeObject(e.getKey());
            s.writeObject(e.getValue());
            }
        }
    }
    private static final long serialVersionUID = 362498820763181265L;
    //Java.io.Serializable read function: read according to write mode
    //Read the "total capacity, actual capacity, all entries" of the HashMap in turn
    private void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
        // Read in the threshold, loadfactor, and any hidden stuff
        s.defaultReadObject();
        // Read in number of buckets and allocate the bucket array;
        int numBuckets = s.readInt();
        table = new Entry[numBuckets];
        init();  // Give subclass a chance to do its thing.
        // Read in size (number of Mappings)
        int size = s.readInt();
        // Read the keys and values, and put the mappings in the HashMap
        for (int i=0; i<size; i++) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            putForCreate(key, value);
        }
    }
    //Return "total capacity of HashMap"
    int   capacity()     { return table.length; }
    //Returns "load factor of HashMap"
    float loadFactor()   { return loadFactor;   }
}

Description:
Before we get into the details of the code for a HashMap, we need to understand that a HashMap is simply a hash table that resolves hash conflicts by "zipping."
It is also worth noting that there are two parameters that affect the performance of HashMap: initialCapacity and loadFactor. The capacity is the number of buckets in the hash table, and the initial capacity is just the capacity of the hash table when it was created. A load factor is a scale at which a hash table can reach multi-full before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, the internal data structure is reconstructed) so that the hash table will have approximately twice the number of buckets.
Part 2.1 "zipper method" of HashMap
2.1.1 HashMap data store array
Transient Entry [] table;
The key-values in the HashMap are stored in the Entry array.
2.1.2 data structure of data node Entry

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    //Point to the next node
    Entry<K,V> next;
    final int hash;
    //Constructor.
    //Input parameters include "hash value (h)"," key (k)", "value (v)"," next node (n)"
    Entry(int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }
    public final K getKey() {
        return key;
    }
    public final V getValue() {
        return value;
    }
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
    //Determine if the two entries are equal
    //Returns true if the "key" and "value" of both entries are equal.
    //Otherwise, false is returned
    public final boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry e = (Map.Entry)o;
        Object k1 = getKey();
        Object k2 = e.getKey();
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {
            Object v1 = getValue();
            Object v2 = e.getValue();
            if (v1 == v2 || (v1 != null && v1.equals(v2)))
                return true;
        }
        return false;
    }
    //Implement hashCode ()
    public final int hashCode() {
        return (key==null   ? 0 : key.hashCode()) ^
               (value==null ? 0 : value.hashCode());
    }
    public final String toString() {
        return getKey() + "=" + getValue();
    }
    //When an element is added to a HashMap, it calls recordAccess().
    //I'm not doing anything here
    void recordAccess(HashMap<K,V> m) {
    }
    //RecordRemoval () is called when an element is removed from a HashMap.
    //I'm not doing anything here
    void recordRemoval(HashMap<K,V> m) {
    }
}

From this, we can see that Entry is really just a one-way linked list. This is why we say that HashMap resolves hash conflicts by zipping.
Entry implements the map.entry interface, which implements the functions getKey(), getValue(), setValue(V value), equals(Object o), and hashCode(). These are basic functions that read/modify key and value values.
Part 2.2: the constructor for a HashMap
HashMap consists of four constructors

//Default constructor.
public HashMap() {
    //Set the "load factor"
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    //Set the "HashMap threshold", and when the amount of stored data in the HashMap reaches the threshold, the capacity of the HashMap needs to be doubled.
    threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
    //Create an Entry array to hold the data
    table = new Entry[DEFAULT_INITIAL_CAPACITY];
    init();
}
//Specifies the constructor for capacity size and load factor
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //The maximum capacity of a HashMap can only be MAXIMUM_CAPACITY
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    // Find a power of 2 >= initialCapacity
    int capacity = 1;
    while (capacity < initialCapacity)
        capacity <<= 1;
    //Set the "load factor"
    this.loadFactor = loadFactor;
    //Set the "HashMap threshold", and when the amount of stored data in the HashMap reaches the threshold, the capacity of the HashMap needs to be doubled.
&nbs
                

Related articles: