1. LinkedHashMap Overview:
LinkedHashMap is a hash table and link list implementation of Map interface with predictable iteration order. This implementation provides all optional mapping operations and allows null values and null keys to be used. This kind of mapping does not guarantee the order of mapping, especially that it does not guarantee that the order will remain unchanged.
LinkedHashMap implementations differ from HashMap implementations in that it maintains a list of double links running on all entries. This list of links defines the iteration order, which can be insertion order or access order.
Note that this implementation is not synchronous. If multiple threads access the hash map of a link at the same time, and at least one of them modifies the map structurally, it must keep external synchronization.
2. Implementation of LinkedHashMap:
For LinkedHashMap, it inherits from HashMap and uses hash tables and two-way linked lists at the bottom to save all elements. Its basic operation is similar to the parent HashMap, which realizes its link list feature by rewriting the parent-related methods. Now let's analyze the source code of LinkedHashMap:
1) Entry element:
LinkedHashMap uses the same hash algorithm as HashMap, but it redefines the element Entry saved in the array, which not only saves the reference of the current object, but also saves the reference of the previous element before and the next element after, thus forming a two-way linked list on the basis of the hash table. Look at the source code:
1 /** 2 * The header element of a two-way linked list. 3 */ 4 private transient Entry<K,V> header; 5 6 /** 7 * LinkedHashMap The Entry element of. 8 * The Entry element of HashMap is inherited, and references to the previous element before and the next element after are saved. 9 */ 10 private static class Entry<K,V> extends HashMap.Entry<K,V> { 11 Entry<K,V> before, after; 12 ...... 13 }
2) Initialization:
From the source code, we can see that in the construction method of LinkedHashMap, we actually call the related construction method of the parent HashMap to construct an array of table s stored at the bottom. Such as:
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; }
Relevant construction methods in HashMap:
1 public HashMap(int initialCapacity, float loadFactor) { 2 if (initialCapacity < 0) 3 throw new IllegalArgumentException("Illegal initial capacity: " + 4 initialCapacity); 5 if (initialCapacity > MAXIMUM_CAPACITY) 6 initialCapacity = MAXIMUM_CAPACITY; 7 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 8 throw new IllegalArgumentException("Illegal load factor: " + 9 loadFactor); 10 11 // Find a power of 2 >= initialCapacity 12 int capacity = 1; 13 while (capacity < initialCapacity) 14 capacity <<= 1; 15 16 this.loadFactor = loadFactor; 17 threshold = (int)(capacity * loadFactor); 18 table = new Entry[capacity]; 19 init(); 20 }
We already know that the Entry element of LinkedHashMap inherits the Entry of HashMap and provides the function of a two-way linked list. The constructor of the HashMap described above
Finally, init() method is invoked for related initialization. This method is meaningless in the implementation of HashMap. It is only provided to subclasses to implement related initialization calls.
LinkedHashMap overrides the init() method and further implements the initialization of its element Entry after calling the parent class's constructor to complete the construction.
void init() { header = new Entry<K,V>(-1, null, null, null); header.before = header.after = header; }
3) Storage:
LinkedHashMap does not override the input method of the parent HashMap, but rewrites the submethods void addEntry (int hash, K key, V value, int bucket index) and void createEntry (int hash, K key, V value, bucket index) called by the input method of the parent HashMap, which provide their own unique implementation of the two-way link list.
1 void addEntry(int hash, K key, V value, int bucketIndex) { 2 // call create Method, new elements are added to the mapping in the form of two-way linked list. 3 createEntry(hash, key, value, bucketIndex); 4 5 // Policy definitions for deleting the least recently used elements 6 Entry<K,V> eldest = header.after; 7 if (removeEldestEntry(eldest)) { 8 removeEntryForKey(eldest.key); 9 } else { 10 if (size >= threshold) 11 resize(2 * table.length); 12 } 13 } 14 15 void createEntry(int hash, K key, V value, int bucketIndex) { 16 HashMap.Entry<K,V> old = table[bucketIndex]; 17 Entry<K,V> e = new Entry<K,V>(hash, key, value, old); 18 table[bucketIndex] = e; 19 // Calling element's addBrefore Method, add elements to hash, two-way link list. 20 e.addBefore(header); 21 size++; 22 } 23 24 private void addBefore(Entry<K,V> existingEntry) { 25 after = existingEntry; 26 before = existingEntry.before; 27 before.after = this; 28 after.before = this; 29 }
4) read:
LinkedHashMap rewrites the get method of the parent HashMap. Actually, after calling the parent getEntry() method to get the elements found, it determines that when the sorting mode accessOrder is true, it records the access order, adds the latest accessed elements to the header of the two-way linked list, and deletes them from the original position. Because the operation of adding and deleting linked list is constant, it will not cause performance loss.
1 public V get(Object key) { 2 // Calling parent class HashMap Of getEntry()Method, get the element to look for. 3 Entry<K,V> e = (Entry<K,V>)getEntry(key); 4 if (e == null) 5 return null; 6 // Record the order of access. 7 e.recordAccess(this); 8 return e.value; 9 } 10 11 void recordAccess(HashMap<K,V> m) { 12 LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; 13 // If defined LinkedHashMap The order of iteration is access order. 14 // Then delete the elements in the previous location and add the latest accessed elements to the list header. 15 if (lm.accessOrder) { 16 lm.modCount++; 17 remove(); 18 addBefore(lm.header); 19 } 20 }
5) Sorting mode:
LinkedHashMap defines the sorting mode accessOrder, which is a boolean variable, true for access order and false for insertion order.
private final boolean accessOrder;
Normally, there is no need to specify the sort mode, and the iteration order is the insertion order by default. See how LinkedHashMap is constructed, such as:
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; }
These constructions default to specify the sort mode as the insertion order. If you want to construct a LinkedHashMap and intend to save elements in the order from the least recent access to the most recent access (that is, access order), use the following constructor to construct a LinkedHashMap:
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
The iteration order of the hash map is the order in which the entries are last accessed. This mapping is very suitable for building LRU caches. LinkedHashMap provides the removeEldestEntry (Map. Entry < K, V > eldest) method, which is called by put and putAll after inserting new entries into the map. This method can provide an implementation program that removes the oldest item every time a new item is added and returns false by default, so that the mapping will behave like a normal mapping, that is, the oldest element can never be removed.
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
This method usually does not modify the mapping in any way, on the contrary, it allows the mapping to modify itself under the guidance of its return value. It is very convenient to build LRU caches with this mapping, which allows the mapping to reduce memory consumption by deleting old entries.
For example, rewrite this method to maintain the stable state of only 100 entries in this mapping, and delete the oldest entries every time new entries are added.
private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; }