[Java Collection source code analysis] LinkedHashmap source code analysis, linkedhashmap source code

Source: Internet
Author: User
Tags concurrentmodificationexception

[Java Collection source code analysis] LinkedHashmap source code analysis, linkedhashmap source code

Reprinted please indicate the source: http://blog.csdn.net/ns_code/article/details/37867985


Some netizens suggested analyzing the source code of LinkedHashMap, so they spent a night studying it and sharing this article (the last blog post in this series). I hope everyone can learn from each other. It is not difficult to understand the LinkedHashMap source code (of course, it should be based on a good understanding of the HashMap source code ).

Introduction to LinkedHashMap

LinkedHashMap is a subclass of HashMap and has the same storage structure as HashMap. However, it adds a header node of a two-way linked list and concatenates all the nodes put to LinkedHashmap into a two-way linked list, therefore, it retains the node insertion sequence, so that the node output sequence is the same as the input sequence.

LinkedHashMap can be used to implement the LRU algorithm (this will be analyzed in the following source code ).

LinkedHashMap is also non-thread-safe and can only be used in a single-threaded environment.

LinkedHashMap source code analysis

LinkedHashMap source code is as follows (detailed comments are added ):

Package java. util; import java. io. * public class implements HashMap <K, V> extends HashMap <K, V> implements Map <K, V> {private static final long serialVersionUID = 20171124242820219213l; // The head node of the two-way cyclic linked list. The entire shard Ha only has one header in the shMap. // It runs all the entries in the hash table, and does not save the key-value pair in the header, only the reference private transient Entry <K, V> header of the front and back nodes are saved; // the flag of the element sorting rule in the two-way linked list. // The value of accessOrder is false, indicating sorting by insert order // The value of accessOrder is true, indicating sorting by access order private final boolean accessOrder; // call the HashMap constructor to construct the underlying array public writable HashMap (int initialCapacity, float loadFactor) {super (initialCapacity, loadFactor); accessOrder = false; // elements in the linked list are sorted by insertion order by default} // take the default 0.75f public incluhashmap (int initialCapacity) {super (initialCapacity); accessOrder = false ;} // set the loading factor to the default value of 0.75f and the capacity to the default value of 16 public writable has. HMap () {super (); accessOrder = false;} // The constructor that contains the sub-Map. Similarly, the corresponding constructor of HashMap is called public writable HashMap (Map <? Extends K ,? Extends V> m) {super (m); accessOrder = false;} // This constructor can specify the public partition hashmap (int initialCapacity, float loadFactor, boolean accessOrder) {super (initialCapacity, loadFactor); this. accessOrder = accessOrder;} // overwrite the init () method of the parent class (the init method in HashMap is empty ), // this method is called before inserting an element in the constructor of the parent class and Clone or readObject. // an empty bidirectional circular linked list is initialized, and no data is saved in the header knot, the next node of the header node starts to save data. Void init () {header = new Entry <K, V> (-1, null); header. before = header. after = header;} // override the transfer method in HashMap. It is called in the resize method of the parent class. // after resizing, the key-value pair is re-mapped to the new newTable. // This method is overwritten to improve the replication efficiency. // here, the two-way cyclic linked list is fully used for iteration, you do not need to perform a for loop on the underlying array. Void transfer (HashMap. Entry [] newTable) {int newCapacity = newTable. length; for (Entry <K, V> e = header. after; e! = Header; e = e. after) {int index = indexFor (e. hash, newCapacity); e. next = newTable [index]; newTable [index] = e ;}// overwrite the containsValue method in HashMap, // This method is overwritten to improve query efficiency. // query is performed based on the characteristics of a bidirectional cyclic linked list, the outer for loop public boolean containsValue (Object value) {// Overridden to take advantage of faster iterator if (value = null) {for (Entry e = header. after; e! = Header; e = e. after) if (e. value = null) return true;} else {for (Entry e = header. after; e! = Header; e = e. after) if (value. equals (e. value) return true;} return false;} // overwrite the get method in HashMap to obtain the Entry object through the getEntry method. // Note the recordAccess method here. // If the sorting rules of elements in the linked list are sorted by insert order, this method will not do anything, // If the sorting rules of elements in the linked list are sorted by access sequence, move e to the end of the linked list. 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;} // clear the HashMap and restore the two-way linked list to the public void clear () {super. clear (); header. before = header. after = header;} // The Enty data structure. Two more private static class Entry references to the front and back nodes <K, V> extends HashMap. entry <K, V> {// These fields comprise the doubly linked list used for iteration. E Ntry <K, V> before, after; // call the construction method Entry (int hash, K key, V value, HashMap) of the parent class. entry <K, V> next) {super (hash, key, value, next);} // Delete the current Entry private void remove () {before. after = after; after. before = before;} // in a two-way cyclic linked list, insert the current Entry to the front of the existingEntry private void addBefore (Entry <K, V> existingEntry) {after = existingEntry; before = existingEntry. before; before. after = this; after. before = th Is ;}// override the recordAccess method in HashMap (this method is empty in HashMap). // when the put Method of the parent class is called and the inserted key already exists, this method is called. // This method is also called when you call the get method overwritten by LinkedHashmap. // This method provides the implementation of the lru algorithm, it places the recently used Entry at the end of the two-way cyclic linked list. // when the value of accessOrder is true, the get method calls the recordAccess method. // The put method also calls the recordAccess method when overwriting the key-value pair. // they cause the Entry to be recently used, therefore, move it to the end of the two-way linked list void recordAccess (HashMap <K, V> m) {LinkedHashMap <K, V> lm = (LinkedHashMap <K, V>) m; // if the elements in the linked list are sorted by access order, move the currently accessed Entry to the end of the two-way cyclic linked list. // If It is sorted by the order of insertion, and nothing is done. If (lm. accessOrder) {lm. modCount ++; // remove the currently accessed Entry remove (); // Insert the currently accessed Entry to the end of the linked list addBefore (lm. header) ;}} void recordRemoval (HashMap <K, V> m) {remove ();}} // Iterator private abstract class extends hashiterator <T> implements Iterator <T> {Entry <K, V> nextEntry = header. after; Entry <K, V> lastReturned = null;/*** The modCount value that the iterator believes that the backing * List shoshould have. if this exp Ectation is violated, the iterator * has detected concurrent modification. */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;} // iterator 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 ;}/// key iterator private class KeyIterator extends hashiterator <K> {public K next () {return nextEntry (). getKey () ;}// value iterator private class ValueIterator extends hashiterator <V> {public V next () {Return nextEntry (). value ;}// Entry iterator private class EntryIterator extends hashiterator <Map. entry <K, V >>{ public Map. entry <K, V> next () {return nextEntry () ;}// These Overrides alter the behavior of superclass view iterator () methods Iterator <K> newKeyIterator () {return new KeyIterator ();} Iterator <V> newValueIterator () {return new ValueIterator ();} Iterator <Map. entry <K, V> newEntry Iterator () {return new EntryIterator ();} // overwrite the addEntry method in HashMap. LinkedHashmap does not overwrite the put Method in HashMap, // overwrites the addEntry and recordAccess methods called by the put method. // The put method calls the recordAccess method when the inserted key already exists, // If the inserted key does not exist, call addEntry to insert a new Entry void addEntry (int hash, K key, V value, int bucketIndex) {// create a new Entry and insert it to createEntry (hash, key, value, bucketIndex) in LinkedHashMap; // the first valid node of the two-way linked list (the node after the header) entry <K, V> Eldest = header. after; // if necessary, delete the least recently used node. // This depends on the overwriting of removeEldestEntry. Because the default value is false, no processing is performed by default. If (removeEldestEntry (eldest) {removeEntryForKey (eldest. key);} else {// resize to the original 2x if (size> = threshold) resize (2 * table. length) ;}} void createEntry (int hash, K key, V value, int bucketIndex) {// create a new Entry, insert it to the header node of the single-chain table corresponding to the array slot. This is the same as HashMap in HashMap. entry <K, V> old = table [bucketIndex]; Entry <K, V> e = new Entry <K, V> (hash, key, value, old ); table [bucketIndex] = e; // each time an Entry is inserted, it is moved to the end of the two-way linked list. // Try inserts LinkedHashMap in sequence to iterate elements. // At the same time, the newly put Entry is the recently accessed Entry, which is placed at the end of the linked list, which conforms to the LRU Algorithm Implementation e. addBefore (header); size ++;} // This method is used for overwriting. Generally, if you use javashashmap to implement the LRU algorithm, you need to override this method, // For example, you can override this method to return true if the set memory is full, so that when you re-submit the method to put // Entry in LinkedHashMap, in the called addEntry method, the least recently used node will be deleted (the node after the header ). Protected boolean removeEldestEntry (Map. Entry <K, V> eldest) {return false ;}}
Summary

The source code of LinkedHashMap is summarized as follows:

1. We can see from the source code that a head header node is added to javashashmap, add all the entries inserted into the LinkedHashMap to the end of the bidirectional cyclic linked list with the head node in sequence.


It is actually a combination of the storage structures of the two collection classes HashMap and consumer list. In LinkedHashMapMap, all put entries are saved in the hash table shown in the first figure, but it also defines an empty two-way circular linked list with head as the header node, every time you put the Entry in, in addition to saving it to the corresponding position in the hash table, you also need to insert it to the end of the bidirectional cyclic linked list.

2. Because LinkedHashMap inherits from HashMap, it has all the features of HashMap and allows the key and value to be null.

3. Pay attention to the accessOrder flag in the source code. When it is set to false, the elements in the two-way linked list are sorted in the order in which the LinkedHashMap is inserted into the Entry, that is to say, the entries in put to LinkedHashMap are placed at the end of the two-way linked list. In this way, when traversing the two-way linked list, the Entry output sequence is consistent with the insert sequence, this is also the storage order of the default two-way linked list. When it is true, it indicates that elements in the two-way linked list are arranged in the order of access, although the order in which entries are inserted to the linked list is still in the order in which they are put to LinkedHashMap, both the put and get Methods call the recordAccess method (the put method is the same as the key, overwrite the original Entry and call the recordAccess method). This method determines whether the accessOrder is true. If yes, the currently accessed Entry (put Entry or get Entry) is used) move to the end of the two-way linked list (the key is not the same, put the new Entry AddEntry will be called, it will call creatEntry, this method will also put the newly inserted elements into the end of the two-way linked list, both in the order of insertion, but also in the order of access, because the Entry is also accessed), otherwise, nothing will be done.

4. Note the constructor. The first four constructor sets accessOrder to false, indicating that the values are sorted by insertion order by default. The fifth constructor can customize the value of the passed accessOrder, therefore, you can specify the sorting rules for elements in a two-way cyclic linked list. Generally, to implement the LRU algorithm using javashashmap, you must use this constructor to set accessOrder to true.

5. LinkedHashMap does not overwrite the put Method in HashMap, but overwrites the addEntry method and recordAccess method called in the put method. Let's look back at the put Method of HashMap:

// Add "key-value" to the HashMap public V put (K key, V value) {// If "key is null ", add the key-value pair to table [0. If (key = null) return putForNullKey (value); // if "key is not null", the hash value of the key is calculated, add it to the linked list corresponding to the hash value. Int hash = hash (key. hashCode (); int I = indexFor (hash, table. length); for (Entry <K, V> e = table [I]; e! = Null; e = e. next) {Object k; // if the key-value pair corresponding to the "key" already exists, replace the old value with the new value. Then exit! If (e. hash = hash & (k = e. key) = key | key. equals (k) {V oldValue = e. value; e. value = value; e. recordAccess (this); return oldValue ;}// if the key-value pair corresponding to the key does not exist, add "key-value" to the table modCount ++; // Add key-value to addEntry (hash, key, value, I) at table [I]; return null ;}
When the key of the Entry to be put already exists in the hash table, the recordAccess method is called. If the key does not exist, the addEntry method is called to insert the new Entry to the header of the single-chain table in the corresponding slot.

Let's first look at the recordAccess method:

// Override the recordAccess method in HashMap (this method is empty in HashMap). // when the put Method of the parent class is called and the inserted key already exists, this method is called. // This method is also called when you call the get method overwritten by LinkedHashmap. // This method provides the implementation of the lru algorithm, it places the recently used Entry at the end of the two-way cyclic linked list. // when the value of accessOrder is true, the get method calls the recordAccess method. // The put method also calls the recordAccess method when overwriting the key-value pair. // they cause the Entry to be recently used, therefore, move it to the end of the two-way linked list void recordAccess (HashMap <K, V> m) {LinkedHashMap <K, V> lm = (LinkedHashMap <K, V>) m; // if the elements in the linked list are sorted by access order, move the currently accessed Entry to the end of the two-way circular linked list. // if the elements are inserted Sort in sequence, and do not do anything. If (lm. accessOrder) {lm. modCount ++; // remove the currently accessed Entry remove (); // Insert the currently accessed Entry to the end of the linked list addBefore (lm. header );}}
This method determines whether the value of accessOrder is true. If the value is true, it moves the currently accessed Entry (the put Entry here) to the end of the bidirectional cyclic linked list, in this way, elements in the two-way linked list are sorted by the access order (the recently accessed Entry is placed at the end of the linked list, so that the elements that have not been accessed recently are located at the beginning after multiple times, in implementation and LRU algorithm, when the number of nodes in the two-way linked list reaches the maximum value, you can delete the previous element because the previous element is least recently used. Otherwise, nothing will be done.
Let's take a look at the addEntry method:

// Override the addEntry method in HashMap. LinkedHashmap does not overwrite the put Method in HashMap. // it overwrites the addEntry method and recordAccess method called by the put method, // when the inserted key already exists, the put method calls the recordAccess method. // If the inserted key does not exist, call addEntry to insert a new Entry void addEntry (int hash, K key, V value, int bucketIndex) {// create a new Entry and insert it to createEntry (hash, key, value, bucketIndex); // the first valid node (the node after the header) of the two-way linked list is the node Entry <K, V> eldest = header. after; // if necessary, delete the least recently used Node, // This depends on the overwriting of removeEldestEntry. Because the default value is false, no processing is performed by default. If (removeEldestEntry (eldest) {removeEntryForKey (eldest. key);} else {// resize to the original 2x if (size> = threshold) resize (2 * table. length) ;}} void createEntry (int hash, K key, V value, int bucketIndex) {// create a new Entry, insert it to the header node of the single-chain table corresponding to the array slot. This is the same as HashMap in HashMap. entry <K, V> old = table [bucketIndex]; Entry <K, V> e = new Entry <K, V> (hash, key, value, old ); table [bucketIndex] = e; // each time an Entry is inserted, it is moved to the end of the two-way linked list. // This will iterate the elements according to the order in which the Entry is inserted into the LinkedHashMap, // At the same time, the newly put Entry is the recently accessed Entry, which is placed at the end of the linked list and conforms to the LRU Algorithm Implementation e. addBefore (header); size ++ ;}
Insert the new Entry to the header of the corresponding single-linked table in the corresponding slot of the table. However, in createEntry, insert the newly put Entry to the end of the two-way linked list. In terms of the insertion sequence, the new Entry is inserted to the end of the two-way linked list, the Entry can be iterated in the order of insertion. In terms of access order, the newly put Entry is the recently accessed Entry, and it should also be placed at the end of the bidirectional linked list.

There is also a removeEldestEntry method above, which is as follows:

// This method is used for overwriting. Generally, if you use javashashmap to implement the LRU algorithm, you need to override this method. // For example, you can override this method if the specified memory is full, returns true. In this way, when the put // Entry in LinkedHashMap is sent again, the least recently used node (the node after the header) will be deleted from the addEntry method called ). Protected boolean removeEldestEntry (Map. Entry <K, V> eldest) {return false ;}}
This method returns false by default. We generally need to override this method when implementing the LRU algorithm using LinkedHashMap. The general implementation is that when the set memory (here refers to the number of nodes) when the maximum value is reached, true is returned. In this way, the removeEntryForKey method is called when a new Entry (the key of this Entry does not exist in the hash table) is put, delete the least recently used node (the node after the head is actually not used recently ).
6. LinkedHashMap overwrites the get method of HashMap:

// Overwrite the get method in HashMap and use the getEntry method to obtain the Entry object. // Note the recordAccess method here. // If the sorting rules of elements in the linked list are sorted by insert order, this method will not do anything, // If the sorting rules of elements in the linked list are sorted by access sequence, move e to the end of the linked list. 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 ;}
Obtain the Entry first. If it is not null, call the recordAccess method as well. The above is clear and I will not explain it here.

7. Finally, let's talk about how LinkedHashMap implements LRU. First, when the value of accessOrder is true, the access order sorting mode is enabled to implement the LRU algorithm. We can see that both the put method and the get method will make the target Entry the recently accessed Entry, therefore, the Entry is added to the end of the two-way linked list (the get method is implemented by calling the recordAccess method. The put method is also implemented by calling the recordAccess method when the existing key is overwritten, when a new Entry is inserted, it is implemented through the addBefore method in createEntry). In this way, the recently used Entry is placed behind the two-way linked list. After multiple operations, the Entry at the front of the two-way linked list is not used recently. When the number of nodes is full, the Entry at the top of the list to be deleted (the Entry at the end of the head) is the most recently used Entry.


Java source code analysis

Awt, the buddy uses java for CS tolerance...
Public class userRegister implements ActionListener {
ArrayList <User> list = new ArrayList <User> ();
JTextField jtf1 = new JTextField (14 );
JTextField jtf2 = new JTextField (14 );
JTextField jtf3 = new JTextField (14 );
JTextField jtf4 = new JTextField (14 );
JFrame jf;
Public userRegister () throws Exception {
CreateGUI ();
}
No one is talking about these codes. Who is using these codes .....

The LRU algorithm of javashashmap in java does not actually change the actual sorting of elements after multiple accesses.

HashMap. get (2) is not the third element !! Alas

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.