Hashmap storage mechanism

HashMap is an asynchronous implementation of the Hashtable-based Map interface. This implementation provides all optional map operations and allows null values ​​and null keys. This class does not guarantee the order of the map, in particular it does not guarantee that the order will be permanent.

In the Java programming language, there are two basic structures, one is an array, and the other is a pointer (reference). HashMap is implemented through these two data structures. HashMap is actually a "linked list hash" data structure, that is, a combination of an array and a linked list.
write picture description here

As shown in the figure above, the bottom layer of the hashmap is an array, and each item is a linked list. When a new HashMap is created, an array is initialized.

Interpretation of core methods

storage

/**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
public V put(K key, V value) {
        //其允许存放null的key和null的value,当其key为null时,调用putForNullKey方法,放入到table[0]的这个位置
        if (key == null)
            return putForNullKey(value);
        //通过调用hash方法对key进行哈希,得到哈希之后的数值。该方法实现可以通过看源码,其目的是为了尽可能的让键值对可以分不到不同的桶中
        int hash = hash(key);
        //根据上一步骤中求出的hash得到在数组中是索引i
        int i = indexFor(hash, table.length);
        //如果i处的Entry不为null,则通过其next指针不断遍历e元素的下一个元素。
        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;
}

Let's take a look at the standard comments of the method: first mentioned in the comments, when we put, if the key exists, the new value will replace the old value, and if the key exists, the method returns the old value value, or null if key does not exist.
It can be seen from the above source code: when we put an element into the HashMap, we first recalculate the hash value according to the hashCode of the key, and obtain the position (ie subscript) of the element in the array according to the hash value. If there are other elements already stored in the position, then the elements in this position will be stored in the form of a linked list, the newly added element is placed at the head of the chain, and the first added element is placed at the end of the chain. If there is no element at this position in the array, the element is directly placed at that position in this array.

read

/**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }
    final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key);
        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;
    }

It can be seen from the above source code: when getting elements from HashMap, first calculate the hashCode of the key, find an element in the corresponding position in the array, and then use the equals method of the key to find the required element in the linked list of the corresponding position.

Summarize

Simply put, HashMap treats key-value as a whole at the bottom, and this whole is an Entry object. The bottom layer of HashMap uses an Entry[] array to store all key-value pairs. When an Entry object needs to be stored, its storage location in the array will be determined according to the hash algorithm, and its location in the array will be determined according to the equals method. The storage location in the linked list of ; when an Entry needs to be taken out, its storage location in the array will be found according to the hash algorithm, and then the Entry will be taken out from the linked list at that location according to the equals method.

Guess you like

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