Java8's understanding of the underlying implementation principle of hashmap

Personal understanding of hashmap, share with you, if there is something wrong, please correct me.

The underlying data structure of the hashmap in java8 is the Node array, and the data structure of the Node array is the linked list. When the chain is expressed to a certain length (8), it will be converted into a red-black tree. This is why the hashmap in java8 is composed of array + linked list + red and black Tree composition.

Then let’s talk about the specific implementation process.
1.
When the put operation is performed, the key.hashcode is first obtained according to the hash method, and then the index is obtained through the modulus operation (table[table.length-1 & hash]) according to the hashcode. , When it is found that the current index has no value, directly store the kv put in at the current position; then if the current index position has a value (that is, the k.hasdcode of the data to be pushed is the same as a certain k.hashcode in the array). It is necessary to compare whether the keys are also the same by equal. If the keys are the same, it means that the keys are the same, and then the original old value needs to be replaced with the new value;
there is another case, when the k.hashcode is the same, but the key is not the same This situation is called hash collision. In this case, the solution is to continue adding kv under the node linked list pointed to by the current hashcode. If the length of the linked list exceeds 8, the current linked list is automatically converted to a red-black tree for storage. Why turn to a red-black tree? Because the red-black tree is faster than the linked list, friends who are familiar with the algorithm should know that the time complexity of the linked list is O(n), and the time complexity of the red-black tree is O(logn).
The above also explains why the key must override the equals method and the hashcode method when using the map.

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    
    
    HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
    // 1.如果table为空或者长度为0,即没有元素,那么使用resize()方法扩容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 2.计算插入存储的数组索引i,此处计算方法同 1.7 中的indexFor()方法
    // 如果数组为空,即不存在Hash冲突,则直接插入数组
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    // 3.插入时,如果发生Hash冲突,则依次往下判断
    else {
    
    
        HashMap.Node<K,V> e; K k;
        // a.判断table[i]的元素的key是否与需要插入的key一样,若相同则直接用新的value覆盖掉旧的value
        // 判断原则equals() - 所以需要当key的对象重写该方法
        if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        // b.继续判断:需要插入的数据结构是红黑树还是链表
        // 如果是红黑树,则直接在树中插入 or 更新键值对
        else if (p instanceof HashMap.TreeNode)
            e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // 如果是链表,则在链表中插入 or 更新键值对
        else {
    
    
            // i .遍历table[i],判断key是否已存在:采用equals对比当前遍历结点的key与需要插入数据的key
            //    如果存在相同的,则直接覆盖
            // ii.遍历完毕后任务发现上述情况,则直接在链表尾部插入数据
            //    插入完成后判断链表长度是否 > 8:若是,则把链表转换成红黑树
            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;
            }
        }
        // 对于i 情况的后续操作:发现key已存在,直接用新value覆盖旧value&返回旧value
        if (e != null) {
    
     // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    // 插入成功后,判断实际存在的键值对数量size > 最大容量
    // 如果大于则进行扩容
    if (++size > threshold)
        resize();
    // 插入成功时会调用的方法(默认实现为空)
    afterNodeInsertion(evict);
    return null;
}

2.
Get get is relatively simple, get the hashcode through the key, and then also get the index according to the hashcode modulo operation (table[table.length-1 & hash]), and then get the corresponding value in the node array according to the index, there is here One situation is the previous hash collision, and the value is stored in the linked list or the red-black tree. At this time, it is necessary to traverse and compare the keys in turn to extract the corresponding value. Here, the red-black tree plays a fast role.

The above is some personal understanding of hashmap.

Guess you like

Origin blog.csdn.net/qq_41454044/article/details/110642514