[Source code] LinkedHashMap source code analysis, linkedhashmap source code
Note: The following source code is based on jdk1.7.0 _ 11
The previous two articles analyzed two common Map sets, HashMap and Hashtable through source code. This article will continue to introduce another Map set-LinkedHashMap. As the name suggests, LinkedHashMap is not only a HashMap, but also has the feature of the shortlist. That is to say, it can maintain the consistent traversal order and insertion order. How can it be done? Next, let's start the analysis. First, let's look at the constructor.
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
LinkedHashMap directly inherits from HashMap, so it has most of the features of HashMap, such as support for null keys and values, default capacity of 16, load factor of 0.75, non-thread security, and so on. However, LinkedHashMap has many other characteristics. Let's look at the member variables below:
Private transient Entry <K, V> header; // the header node of the internal two-way linked list/*** indicates the sorting method of the linked list. true indicates the order of access, false indicates the order of insertion. */Private final boolean accessOrder;
LinkedHashMap has two member variables more than HashMap, And the header represents the header node of the internal two-way linked list. Later we will find that LinkedHashMap contains all the entries except a bucket array, there is also a two-way linked list to save all Entry references. Times
During the calendar, instead of traversing the bucket array, it directly traverses the two-way linked list. Therefore, the Traversal Time of LinkedHashMap is not limited by the bucket capacity, which is one of the important differences between it and HashMap.
However, this accessOrder indicates whether to follow the access order. true indicates yes. The default value is the insert order. Therefore, we can set accessOrder to true to implement the LRU algorithm, which can be used for caching.Let's look at the constructor again:
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(m); accessOrder = false; } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
The constructor will first call the parent class, that is, the HashMap constructor, to initialize the bucket array, and the accessOrder will be initialized later. Apart from the last constructor, accessOrder can be specified, all other constructors set accessOrder to false by default. The reader may be surprised. Isn't there a header? Why is this two-way linked list not initialized in the constructor? You have to go back to HashMap to view the hashMap constructor:
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); ... ... init(); }
The HashMap constructor calls an init method in the last step. The init method is an empty implementation in HashMap without any code.
This is actually a so-called "Hook". The specific code is implemented by sub-classes. If the sub-classes want to perform some specific initialization operations each time they are constructed, you can re-write the init method.We can see that the LinkedHashMap indeed rewrites init:
@ Override void init () {header = new Entry <> (-1, null); // initialize the two-way linked list header. before = header. after = header; // not only a two-way linked list, or a circular linked list}
In the init method, a two-way linked list is initialized, and we also find that this is not just a two-way linked list, but a circular linked list.
The Entry class inside HashMap does not have before and after pointers,
That is to say, LinkedHashMap overwrites an Entry class.:
Private static class Entry <K, V> extends HashMap. entry <K, V> {// These fields comprise the doubly linked list used for iteration. entry <K, V> before, after; // precursor, successor pointer Entry (int hash, K key, V value, HashMap. entry <K, V> next) {super (hash, key, value, next);}/*** Removes this entry from the linked list. */private void remove () {before. after = after; after. before = before;}/*** Inserts this entry before the specified existing entry in the list. */private void addBefore (Entry <K, V> existingEntry) {after = existingEntry; before = existingEntry. before; before. after = this; after. before = this;}/*** This method is invoked by the superclass whenever the value * of a pre-existing entry is read by Map. get or modified by Map. set. * If the enclosing Map is access-ordered, it moves the entry * to the end of the list; otherwise, it does nothing. */void recordAccess (HashMap <K, V> m) {LinkedHashMap <K, V> lm = (LinkedHashMap <K, V>) m; if (lm. accessOrder) {lm. modCount ++; remove (); addBefore (lm. header) ;}} void recordRemoval (HashMap <K, V> m) {remove ();}}
The Entry here selects the Entry class that inherits the parent class, that is
The Entry in LinkedHashMap has three pointers. In addition to the frontend and successor pointers, it is used for the connection of two-way linked lists, and a next pointer is used to solve the hash conflict (reference chain ).In addition, several methods are added to the Entry. remove and addbefore are not required to operate two-way linked lists. The recordAccess method is special. This method is also empty in HashMap and will be called in the put method:
Public V put (K key, V value) {// put Method of HashMap if (key = null) return putForNullKey (value); int hash = hash (key ); 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); // this method is called When overwrite is performed. return oldValue ;}}......}
In addition, this method is also called in the get method of LinkedHashMap:
public V get(Object key) { Entry<K,V> e = (Entry<K,V>)getEntry(key); if (e == null) return null; e.recordAccess(this); return e.value; }
That is to say, as long as the Access Node is involved, this method will be called.
Observe the logic of this method: If accessOrder is true, the addBefore method will be called to put the current Entry at the end of the two-way linked list, in the end, we will find that the least recently used nodes are concentrated in the head of the linked list (from the last visit to the most recent visit), which is LRU.
LinkedHashMap does not rewrite the put method, but it re-writes the addEntry and createEntry methods. We knew it before analyzing HashMap, the put method will call addEntry to mount the key-value pair to a proper location in the bucket, and addEntry will call the createEntry method to create a key-Value Pair object. Therefore, LinkedHashMap indirectly changes the put method. It is easy to think about it. In addition to adding a key value to the bucket, LinkedHashMap also needs to add a key-value pair to the linked list, therefore, you must modify the put method.
Void addEntry (int hash, K key, V value, int bucketIndex) {super. addEntry (hash, key, value, bucketIndex); // Remove eldest entry if your ucted Entry <K, V> eldest = header. after; // mark the least accessed object if (removeEldestEntry (eldest) {// determine whether to delete this object ----> the cache function removeEntryForKey (eldest. key) ;}} void createEntry (int hash, K key, V value, int bucketIndex) {HashMap. entry <K, V> old = table [bucketIndex]; Entry <K, V> e = new Entry <> (hash, key, value, old ); table [bucketIndex] = e; e. addBefore (header); // Add to the end of the linked list size ++ ;}
The createEntry method mounts key-value pairs to the bucket array and two-way linked list respectively.
The addEntry method provides an optional operation, we can inherit the LinkedHashMap and re-write the removeEldestEntry method so that this subclass can automatically delete the least recently accessed key-value pairs-this can be used for caching !!
LinkedHashMap defines the iterator and iteration rules. LinkedHashMap completes iteration through an internal two-way linked list. The Traversal Time is proportional to the total number of key-value pairs, while the HashMap Traversal Time is proportional to the capacity, therefore, the traversal performance of LinkedHashMap is generally better than that of HashMap. However, because additional maintenance of the linked list is required, the performance of the two is almost the same.
Private abstract class extends hashiterator <T> implements Iterator <T> {Entry <K, V> nextEntry = header. after; // point to the first Entry of the linked list <K, V> lastReturned = null; int expectedModCount = modCount; public boolean hasNext () {return nextEntry! = Header;} public void remove () {if (lastReturned = null) throw new IllegalStateException (); if (modCount! = ExpectedModCount) throw new ConcurrentModificationException (); LinkedHashMap. this. remove (lastReturned. key); lastReturned = null; expectedModCount = modCount;} Entry <K, V> nextEntry () {if (modCount! = ExpectedModCount) throw new ConcurrentModificationException (); if (nextEntry = header) throw new NoSuchElementException (); Entry <K, V> e = lastReturned = nextEntry; nextEntry = e. after; return e ;}}
Summary:
1. LinkedHashMap inherits from HashMap and has most of the features of HashMap. For example, it supports null keys and values, the default capacity is 16, the load factor is 0.75, and non-thread security;
2. LinkedHashMap sets accessOrder to control whether the traversal order is insert or access order. When accessOrder is true, you can use it to complete the LRU cache function;
3. LinkedHashMap maintains a two-way circular linked list internally and completes its iteration through the linked list instead of traversing the hash table.
Can beginners understand STL source code analysis?
STL source code analysis does not explain how to use STL and STL techniques, but analyzes STL core code, it is designed for experienced STL programmers to supplement and better understand the underlying core mechanism of STL. It is basically confusing for beginners to read this book. It is recommended that you start from the basics first, C ++ standard library and C ++ stl are good beginners and used books. After some STL experience, I will study STL source code analysis, I believe that you will have another understanding of STL at that time.
Error Message