LinkedHashMap source code analysis of Java Collection series, linkedhashmap source code

Source: Internet
Author: User

LinkedHashMap source code analysis of Java Collection series, linkedhashmap source code

In this article, we started to analyze the source code of LinkedHashMap. LinkedHashMap inherits HashMap, which means that LinkedHashMap is extended on the basis of HashMap. Therefore, before reading the source code of LinkedHashMap, it is necessary for readers to first understand the source code of HashMap, you can refer to the introduction to Java Collection series [3] ---- HashMap source code analysis in my previous article. As long as you thoroughly understand the implementation principles of HashMap, it is very simple to go back and look at the source code of LinkedHashMap, HashSet, and LinkedHashSet. Therefore, readers should study the HashMap source code with patience. This is a good business to buy one get three. In the previous analysis of the HashMap source code, I used problem-oriented analysis of the source code, so that I could not analyze the source code in disorder like a headless fly, and readers could have a deeper understanding of the problem. In this article, I decided to use this method to analyze LinkedHashMap.

1. What structure does javashashmap use internally?

As you can see, because LinkedHashMap inherits from HashMap, LinkedHashMap still has a hash table, but LinkedHashMap re-writes an Entry and adds two member variables to the original HashMap Entry, it is a forward node reference and a successor node reference. In this way, all nodes are linked together to form a two-way linked list. When getting elements, you can directly traverse the two-way linked list. Let's see what the Entry implemented by LinkedHashMap looks like.

Private static class Entry <K, V> extends HashMap. entry <K, V >{// reference Entry of the forward node of the current node in the two-way linked list <K, V> before; // reference Entry <K, V> after; Entry (int hash, K key, V value, HashMap. entry <K, V> next) {super (hash, key, value, next);} // remove the node private void remove () {before from the two-way linked list. after = after; after. before = before;} // Insert the current node to an existing node in the two-way linked list. private void addBefore (Entry <K, V> existingEntry) {// reference of the next node of the current node points to the given node after = existingEntry; // reference of the previous node of the current node points to the previous node before = existingEntry of the given node. before; // reference of the next node of the previous node of the given node points to the current node before. after = this; // the reference of the previous node of the given node points to the current node after. before = this;} // record the void recordAccess (HashMap <K, V> m) {LinkedHashMap <K, v> lm = (LinkedHashMap <K, V>) m; // if it is sorted by access order if (lm. accessOrder) {lm. modCount ++; // first remove yourself from the two-way linked list (); // put yourself at the end of the two-way linked list addBefore (lm. header) ;}} void recordRemoval (HashMap <K, V> m) {remove ();}}

2. How does LinkedHashMap sort by insert order?

// Void addEntry (int hash, K key, V value, int bucketIndex) that will be called in the put Method of the parent class {// call the addEntry method super of the parent class. addEntry (hash, key, value, bucketIndex); // The following operations facilitate the implementation of LRU cache. If the cache capacity is insufficient, remove the oldest element Entry <K, v> eldest = header. after; if (removeEldestEntry (eldest) {removeEntryForKey (eldest. key) ;}} // The addEntry method of the parent class will call this method void createEntry (int hash, K key, V value, int bucketIndex) {// obtain the Entry HashMap of HashMap first. entry <K, V> old = table [bucketIndex]; // The Entry <K, V> e = new Entry <> (hash, key, value, old); table [bucketIndex] = e; // Insert the current node to the end of the two-way linked list e. addBefore (header); size ++ ;}

LinkedHashMap overwrites the addEntry and createEntry methods of its parent HashMap class. When you want to insert a key-value pair, the put method of its parent HashMap class is called first. In the put method, check whether the corresponding key exists in the hash table. If yes, replace the value directly, if it does not exist, call the addEntry method to create a new Entry. Note: At this time, the addEntry method of LinkedHashMap is called. We can see the above Code. In addition to the addEntry method of the parent class, this addEntry method also calls removeEldestEntry to remove the oldest element. This step is mainly used to implement the LRU algorithm. We can see that LinkedHashMap also overwrites the createEntry method. This method will be called when a new Entry is to be created. The createEntry method puts the Entry into the hash table each time, the addBefore method is called to insert the current node to the end of the two-way linked list. In this way, the two-way linked list records the order of each inserted node. You only need to traverse the two-way linked list to obtain the element. This shows the operation of calling addBefore each time. Because it is a two-way linked list, the current node is inserted to the end of the two-way linked list before being inserted to the first node.

3. How to Use LinkedHashMap to implement LRU cache?

We know that the implementation of the cache depends on the computer memory, and the memory resources are quite limited, so it is impossible to store elements without limit. Therefore, we need to delete some elements when the capacity is insufficient, which element is better to delete? The concept of the LRU algorithm is that if a data has been recently accessed, it will have a higher chance of being accessed in the future. Therefore, we can delete data that is not frequently accessed. Next, let's take a look at how the LRU mechanism is implemented in LinkedHashMap.

Public class extends HashMap <K, V> extends HashMap <K, V> implements Map <K, V> {// bidirectional linked list header node private transient Entry <K, V> header; // sort private final boolean accessOrder by access order ;... public writable hashmap (int initialCapacity, float loadFactor, boolean accessOrder) {super (initialCapacity, loadFactor); this. accessOrder = accessOrder;} // obtain the value public V get (Object key) based on the key {// call the parent class method to obtain the Entry corresponding to the key <K, v> e = (Entry <K, V>) getEntry (key); if (e = null) {return null;} // if it is sorted by access order, the node used each time will be placed at the end of the two-way linked list e. recordAccess (this); return e. value;} private static class Entry <K, V> extends HashMap. entry <K, V> {... // Insert the current node to an existing node in the two-way linked list. private void addBefore (Entry <K, V> existingEntry) {// reference of the next node of the current node points to the given node after = existingEntry; // reference of the previous node of the current node points to the previous node before = existingEntry of the given node. before; // reference of the next node of the previous node of the given node points to the current node before. after = this; // the reference of the previous node of the given node points to the current node after. before = this;} // record the void recordAccess (HashMap <K, V> m) {LinkedHashMap <K, v> lm = (LinkedHashMap <K, V>) m; // if it is sorted by access order if (lm. accessOrder) {lm. modCount ++; // first remove yourself from the two-way linked list (); // put yourself at the end of the two-way linked list addBefore (lm. header );}}...} // void addEntry (int hash, K key, V value, int bucketIndex) that will be called in the put Method of the parent class {// call the addEntry method super of the parent class. addEntry (hash, key, value, bucketIndex); // The following operations facilitate the implementation of LRU cache. If the cache capacity is insufficient, remove the oldest element Entry <K, v> eldest = header. after; if (removeEldestEntry (eldest) {removeEntryForKey (eldest. key) ;}} // whether to delete the oldest element. This method is designed to overwrite protected boolean removeEldestEntry (Map. entry <K, V> eldest) {return false ;}}

To be more intuitive, I omitted some irrelevant code in the code above. We can see that javashashmap has a member variable accessOrder, which records whether the member variables need to be sorted by access order, it provides a constructor that can specify its own accessOrder value. E. recordAccess (this) is called every time you call the get method to obtain the element type. this method moves the current node to the end of the two-way linked list. Now we know that if accessOrder is true, this element will be moved to the end of the two-way linked list every time the get element is obtained. The purpose of this step is to distinguish between the most commonly used elements and infrequently used elements. The frequently used elements are placed at the end, and the infrequently used elements are put at the header. We can go back to the above Code and see that every time we call the addEntry method, we will determine whether to delete the oldest element. The judgment logic is implemented by removeEldestEntry. This method is designed to overwrite and override the logic. Note: Because recently accessed nodes are moved to the end of the two-way linked list, the oldest node is removed from the head of the two-way linked list and deleted. The following example implements a simple LRU cache.

Public class LRUMap <K, V> extends hashmap <K, V> {private int capacity; LRUMap (int capacity) {// call the parent class constructor, set to super (capacity, 1f, true) in the order of access; this. capacity = capacity;} @ Override public boolean removeEldestEntry (Map. entry <K, V> eldest) {// return this when the key-value pair is greater than or equal to the hash table capacity. size ()> = capacity;} public static void main (String [] args) {LRUMap <Integer, String> map = new LRUMap <Integer, String> (4); map. put (1, "a"); map. put (2, "B"); map. put (3, "c"); System. out. println ("original set:" + map); String s = map. get (2); System. out. println ("Get element:" + map); map. put (4, "d"); System. out. println ("after insertion:" + map );}}

The result is as follows:

Note: All the above analyses are based on JDK1.7, which may vary with different versions.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

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.