Chapter 11 LinkedHashMap source code analysis of Java collections

Preface

LinkedHashMap is a subclass of HashMap, which inherits the methods of HashMap and expands its capabilities. Therefore, this chapter only analyzes the extended part of LinkedHashMap. As for the related parts of HashMap, please refer to the following entry:
Chapter 10 Java Collection Analysis of HashMap Source Code from Design Ideas
This chapter is based on JDK 1.8 version.

Expansion capability based on HashMap

LinkedHashMap knows from its name, it has a certain connection with the linked list structure. Those who know HashMap know that HashMap is an unordered storage structure, and the order of internal elements cannot be guaranteed. Therefore, the primary function of LinkedHashMap is to provide an orderly structure. Among the known data structures, linked lists are one of the best implementations.

Orderly

LinkedHashMap internally maintains the node Entry like HashMap. Entry is a subclass of Node in HashMap and inherits the methods and behaviors of Node. The difference between Entry and Node is that it has two more variables before and after:

/**
 * HashMap.Node subclass for normal LinkedHashMap entries.
 */
static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

The birth of before and after means that each Entry node of LinkedHashMap is added to the collection. The hash algorithm is still maintained internally, and the array is maintained internally. The linked list and red-black tree are still maintained on the array, but the current node Entry can interact with The previous node is connected, and it can also be connected to the next node.

The
Insert picture description here
main idea of hashMap linkedHashMap
Insert picture description here
is to add a linked list structure on the basis of HashMap, and maintain the pointers of the head node and the end node of the linked list inside.

transient LinkedHashMap.Entry<K,V> head;

transient LinkedHashMap.Entry<K,V> tail;

The linked list design gives LinkedHashMap an order, but it is specifically stated that there are two orders in the LInkedHashMap, one is the insertion order, and the other is the access order. The mechanism for choosing which order to use is to maintain a variable:

/**
 * The iteration ordering method for this linked hash map: <tt>true</tt>
 * for access-order, <tt>false</tt> for insertion-order.
 *
 * @serial
 */
final boolean accessOrder;

If it is true, it means visit ordering. If it is false, it means insertion sort.
Generally, when the linkedHashMap is initialized in the constructor, the default is the LinkedHashMap object of insertion sort.

 public LinkedHashMap(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor);
    accessOrder = false;
}

 public LinkedHashMap(int initialCapacity) {
    super(initialCapacity);
    accessOrder = false;
}

 public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

Insertion sort

For the linkedHashMap for insertion sort, every new node is added, the front and back nodes will be linked through linkNodeLast to form the insertion order.

linkNodeLast method

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    LinkedHashMap.Entry<K,V> last = tail;
    tail = p;
    if (last == null)
        head = p;
    else {
        p.before = last;
        last.after = p;
    }
}

Access ordering

For insertion sort, it should be understood through the above linked list. The order of insertion is maintained through the doubly linked list. So what is the visit order? When accessing the linkedHashMap, get the element through the get method.

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

From the get method, we can control whether to execute the afterNodeAccess(e) method through accessOrder. What does the afterNodeAccess method do?

void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMap.Entry<K,V> last;
    
	// 如果get方法访问的结点不是最后一个结点,则有可能是头结点和中间结点,则进行如下操作
    if (accessOrder && (last = tail) != e) {
        LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.after = null;
        
        // 下面操作意思就是,将这个e结点从链表中解开,单独剔出来。
        if (b == null)
            head = a;
        else
            b.after = a;
            
        if (a != null)
            a.before = b;
        else
            last = b;
            
         // 将e结点放在链表最后
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        
        tail = p;
        ++modCount;
    }
}

The general meaning of the above is that the element nodes after get will be placed at the end of the linked list in the insertion order. But the internal array structure will not change.

If the following figure is a LInkedHashMap object with access sorting, the current access sorting.
Before the visit
Insert picture description here
Now we have visited the n+1 node.
Using get((n+1)) does not represent a specific value.

After the visit,
Insert picture description here
you can see that the internal position of the element has not changed, but the order of the maintained linked list has changed.

to sum up

The following points are summarized for insertion sort and access sort:
1. Insertion sort and access sort are controlled by the accessOrder variable.
2. Insertion sort is used by default.
3. When using access sorting, insertion sorting still exists.

Guess you like

Origin blog.csdn.net/weixin_43901067/article/details/105293832