Anatomy of the "Java Collection source code" LINKEDHASHMAP source code

Source: Internet
Author: User

Reprint Please specify the Source: http://blog.csdn.net/ns_code/article/details/37867985


The source code of Linkedhashmap is suggested by netizens. Then spent a night studying, sharing this article (the Last Post in this series), I hope you learn from each other. Linkedhashmap source code is not difficult to understand (of course.) To be based on a good understanding of HashMap source code).

Linkedhashmap Brief Introduction

Linkedhashmap is a subclass of HashMap and has the same storage structure as HashMap. But it joins the head node of a doubly linked list. All put to linkedhashmap nodes are strung into a two-way loop linked list. So it preserves the order in which the nodes are inserted. Enables the output sequence of the nodes to be the same as the input order.

Linkedhashmap can be used to implement the LRU algorithm (which is analyzed in the following source code).

Linkedhashmap is the same as non-thread safe. Used only in single-threaded environments.

linkedhashmap Source Code anatomy

Linkedhashmap source code such as the following (added specific gaze):

Package Java.util;import java.io.*;p ublic class Linkedhashmap<k,v> extends hashmap<k,v> implements MAP&L T k,v>{private static final long Serialversionuid = 3801124242820219131l;//The head node of the bidirectional circular list. The whole Linkedha only has a header in the Shmap. It runs through all the Entry in the hash table, does not save the Key-value pair in the header, just saves the reference to the previous node, private transient entry<k,v> header;//the flag bit of the collation of the element in the doubly linked list. Accessorder is false, which means//accessorder is sorted by insertion order.  Represents the order in which private final Boolean accessorder;//calls HashMap to construct the underlying array public linkedhashmap (int initialcapacity, float        Loadfactor) {super (initialcapacity, loadfactor); Accessorder = The elements in the false;//list are sorted by default in order of insertion}//load factor take default 0.75f public linkedhashmap (int initialcapacity) {Super (Initialcapa        City);    Accessorder = false; The}//load factor takes the default 0.75f.        The capacity takes the default of the public Linkedhashmap () {super ();    Accessorder = false;        }//contains sub-Map construction method, the same call HashMap the corresponding construction method public linkedhashmap (map<? extends K,? extends v> m) {super (M);    Accessorder = false; }//the structuremethod to specify the ordering of elements in the list public linkedhashmap (int initialcapacity,float Loadfactor,boolean accessorder) {super (Initia        Lcapacity, Loadfactor);    This.accessorder = Accessorder; }//override the Init () method of the parent class (the Init method in HashMap is empty).    The method is called in the parent class's constructor and clone, readobject before inserting the element,//initializing an empty two-way loop linked list, the head node does not save the data, the next nodes of the head nodes start to save the data.        void init () {header = new entry<k,v> ( -1, NULL, NULL, NULL);    Header.before = Header.after = header; }//overwrite the transfer method in the HashMap.    It is called in the Resize method of the parent class,//After expansion, the Key-value pair is mapped again to the new newtable//The purpose of this method is to improve the efficiency of the replication,//here to make full use of the characteristics of the two-way loop list iteration, do not have to the underlying array for the loop.        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 the HashMap. The purpose of the method is to improve the efficiency of the query,//Use the characteristics of the two-way circular linked list query. The outer for loop of the array is missing the public boolean containsvalue (Object value){//overridden to take advantage of faster iterator if (value==null) {for (Entry e = Header.aft Er 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 the HashMap, and obtain the entry object through the Getentry method. Notice the Recordaccess method here. Assuming that the collation of the elements in the list is sorted according to the order in which they were inserted, the method does nothing, assuming that the ordering rules for the elements in the list are sorted according to the order of the visits.    The E is moved to the end of the 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;        }//empties the HashMap and restores the doubly linked list to the empty linked list with only the head node public void Clear () {super.clear ();    Header.before = Header.after = header; }//enty data structure, two more references to front and back nodes private static class Entry<k,v> extends Hashmap.entry<k,v> {//These fie LDS CoMprise The doubly linked list used for iteration. Entry<k,v> before, after;//calls the constructor method of the parent class Entry (int hash, K key, V value, hashmap.entry<k,v> next) {Supe        R (hash, key, value, next); }//in a two-way loop linked list.            Remove the current entry private void Remove () {before.after = after;        After.before = before;  }//the current Entry into the front of the existingentry private void Addbefore (Entry<k,v> existingentry) {after            = Existingentry;            before = Existingentry.before;            Before.after = this;        After.before = this; }//overwrite the Recordaccess method in the HashMap (the method in HashMap is empty),//When the Put method of the parent class is called, the method is called when the inserted key is found to exist,//when the Linkedhashmap overwrite get method is called. will also be called to the method. This method provides the implementation of the LRU algorithm, which puts the recently used entry to the tail of the bidirectional loop list, and//accessorder to True when the Get method calls the Recordaccess method// The Put method also calls the Recordaccess method when overwriting key-value pairs//They cause entry to be used recently. So move it to the end of the doubly linked list void recordaccess (hashmap<k,v> m) {linkedhashmap<k,v> LM = (linkedhashmap< k,v>) m;//If the elements in the list are sorted in order ofThe entry of the previous interview moves to the end of the two-way circular list, and/or is assumed to be sorted according to the order in which it was inserted, and no matter what.                if (lm.accessorder) {lm.modcount++;//Removes the currently visited entry remove ();//Inserts the entry of the current interview into the tail of the linked list            Addbefore (Lm.header);        }} void Recordremoval (hashmap<k,v> m) {remove ();     }}//iterator private abstract class Linkedhashiterator<t> implements Iterator<t> {entry<k,v> NextEntry = Header.after; entry<k,v> lastreturned = null;/** * The Modcount value, the iterator believes that the backing * List should ha  Ve. If this expectation are 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;} Start iteration from the next node of head entry<k,v> NextEntry () {if (Modcount! = expectedmodcount) throw new Concurrentmodificationexcep            tion ();            if (NextEntry = = header) throw new Nosuchelementexception ();            Entry<k,v> e = lastreturned = NextEntry;            NextEntry = E.after;    return e;}  }//key iterator Private class Keyiterator extends linkedhashiterator<k> {public K next () {return nextentry (). GetKey (); }}//value iterator Private class Valueiterator extends linkedhashiterator<v> {public V next () {return nextentry () . value; }}//entry iterator Private class Entryiterator extends linkedhashiterator<map.entry<k,v>> {public MAP.ENTRY&L T    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>> Newentryiterator () {return new Entryiterator ();} Overwrite the AddEntry method in HashMap, Linkedhashmap did not overwrite the Put method in HashMap,//instead overwrite the AddEntry method and Recordaccess method called by the Put method,// The Put method calls the Recordaccess method in the case where the inserted key already exists. To call AddEntry Insert a new entry void addentry (int hash, K key, V value, int bucketindex) {//Create new entry and insert into LINKEDH if the inserted key does not exist)        Ashmap createentry (hash, key, value, Bucketindex); The first valid node of the doubly linked list (the node after the header) is the least recently used node entry<k,v> eldest = header.after;//If necessary, delete the least recently used node,//This depends on the Removee Ldestentry, because the default is false, so it does not do whatever processing.

if (Removeeldestentry (eldest)) {Removeentryforkey (Eldest.key); } else {//expansion to the original twice times if (size >= threshold) Resize (2 * table.length); }} void Createentry (int hash, K key, V value, int bucketindex) {//Creates a new entry and inserts it into the head node of the single-linked list of the corresponding slots in the array. This is the same as in HashMap hashmap.entry<k,v> old = Table[bucketindex]; entry<k,v> e = new entry<k,v> (hash, key, value, old); Table[bucketindex] = e;//Each time the entry is inserted. are moved to the end of the doubly linked list,//This will iterate through the elements in the order in which the entry is inserted into the linkedhashmap,//At the same time. The new put in entry is a recent visit to entry, put it at the end of the list. Conforms to the LRU algorithm implementation E.addbefore (header); size++; }//The method is used for overwriting, generally assuming that the LRU algorithm is implemented with LINKEDHASHMAP. The method must be covered. For example, the ability to overwrite this method with the assumption that the memory is full, returns true so that when put//entry to Linkedhashmap again, the least recently used node is removed (the node after the header) in the called AddEntry method. Protected Boolean removeeldestentry (map.entry<k,v> eldest) {return false; }}

some summary

The source code for LINKEDHASHMAP. Here are some of the more important summaries:

1, from the source code can be seen. A head node is added to the Linkedhashmap, and the entry inserted into the Linkedhashmap is added to the end of the two-way circular list with head nodes in order of insertion.


is actually a combination of the storage structure of the HashMap and LinkedList two collection classes. In the Linkedhashmapmap. All put-in entry are stored in the hash table as seen in the first diagram. But it also defines an empty two-way circular list with head nodes, each time the put comes in entry, except to save it to the corresponding position in the hash table. It is also inserted into the tail of the bidirectional loop list.

2, Linkedhashmap because inherit from HashMap. So it has all the attributes of HashMap, with the same consent that key and value are null.

    3, Note the ACCESSORDER flag bit in the source code. , that is, every entry put into Linkedhashmap is placed at the end of a doubly linked list, so that when traversing a doubly linked list, The output order of the entry is the same as the order of insertion, which is the default order of the two-way linked list; When it is true, the elements in the doubly linked list are arranged in the order in which they are visited , to see, Although the order in which the entry inserts the list is still in accordance with the order in which it was put to Linkedhashmap, the put and get methods call the Recordaccess method (the Put method is the same as the key, The Recordaccess method is called when overwriting the original entry), and the method infers whether Accessorder is true. Suppose yes, move the current access entry (put in the entry or get out of the entry) to the tail of the doubly linked list (key is not the same time, put a new entry, will call AddEntry, it will call Createntry, The same method puts the newly inserted elements into the tail of the doubly linked list, both in order of insertion and in order of access, as the entry is also interviewed, otherwise, nothing is done.

4, pay attention to the construction method, the first four construction methods are set to False accessorder, indicating that the default is sorted according to the order of insertion , and the fifth construction method can define the value of the incoming Accessorder itself, It is therefore possible to specify the collation of elements in a bidirectional circular list. The LRU algorithm is generally implemented with Linkedhashmap. This construction method is used to set the Accessorder to true.

5. Linkedhashmap did not overwrite the put method in HashMap. Instead of the AddEntry method and the Recordaccess method called in the Put method , we go back and look at the HashMap put method:

//Add "Key-value" to HashMap public V put (K key, V value) {//If "key is nul            L ", the key value pair is added to table[0].            if (key = = null) return Putfornullkey (value); If "key is not NULL". The hash value of the key is computed.            It is then added to the corresponding linked list of 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 corresponding key value pair for this 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 corresponding key-value pair for "key" does not exist, add "key-value" to table modcount++;            Add Key-value to Table[i] place addentry (hash, key, value, I);        return null; }       
When the entry key to put in is already present in the hash table, the Recordaccess method is called when the key does not exist. The AddEntry method is called to insert the new entry into the header of the corresponding slot's single-linked list.

Let's first look at the Recordaccess method:

Overwrite the Recordaccess method in HashMap (the method is empty in HashMap),//When the Put method of the parent class is called. The method is called when the inserted key is found to be present,//Called when the Linkedhashmap override get method is invoked, or the method provides an implementation of the LRU algorithm. It puts the recently used entry at the tail end of the bidirectional loop list and//accessorder to True. The Get method calls the Recordaccess method//put method will also call the Recordaccess method when overwriting the Key-value pair//They cause entry to be used recently, so move it to the end of the doubly linked list        void Recordaccess (hashmap<k,v> m) {            linkedhashmap<k,v> lm = (linkedhashmap<k,v>) m;// Assuming that the elements in the list are sorted in order of access, the entry of the current interview is moved to the end of the two-way circular list, or/or assumed to be sorted according to the order in which they were inserted. Do not do whatever it is.            if (lm.accessorder) {                lm.modcount++;//removes the currently visited entry remove                ();//Inserts the entry of the current interview into the tail of the list                Addbefore ( Lm.header);            }        }
This method infers whether Accessorder is true and, assuming true, moves the currently visited entry (in this case, the put-in entry) to the tail of the doubly-linked loop list. This enables the elements in the doubly linked list to be sorted according to the order of the visits (the recent visit to the entry put to the end of the list, so many times down, the front is not recently visited elements, in the implementation, LRU algorithm. When the number of nodes in a doubly linked list reaches the maximum, delete the previous element, because the previous element is the least recently used, or do nothing.


Then look at the AddEntry method:

Overwrite the AddEntry method in HashMap, Linkedhashmap does not overwrite the Put method in HashMap. Instead, the AddEntry method and the Recordaccess method called by the put method are covered, and the//put method is in the case where the inserted key already exists. The Recordaccess method is called,//In case the inserted key does not exist. To call AddEntry Insert a new entry void addentry (int hash, K key, V value, int bucketindex) {//Create a new entry.        and inserted into the Linkedhashmap createentry (hash, key, value, Bucketindex); The first valid node of the doubly linked list (the node after the header) is the least recently used node entry<k,v> eldest = header.after;//If necessary, remove the least recently used node. It depends on the removeeldestentry of the letter, because it feels false.        So the default is not to do whatever processing.        if (Removeeldestentry (eldest)) {Removeentryforkey (Eldest.key);        } else {//expansion to the original twice times if (size >= threshold) Resize (2 * table.length);        }} void Createentry (int hash, K key, V value, int bucketindex) {//Creates a new entry and inserts it into the head node of the single-linked list of the corresponding slots in the array, which is the same as in HashMap hashmap.entry<k,v> old = Table[bucketindex];        entry<k,v> e = new entry<k,v> (hash, key, value, old); Table[bucketindex] = e;//Each time the entry is inserted, it is moved to the tail of the doubly linked list,//This will be inserted according to entry LinkedhAshmap the order to iterate over elements,//at the same time.        The new put came in the entry is recently visited entry, put it at the end of the list, in line with the implementation of the LRU algorithm E.addbefore (header);    size++; }
The same is the insertion of a new entry into the head node of the corresponding single-linked list in the corresponding slot in the table. But it can be seen that in Createentry, the same entry inserted in the new put into the tail of the doubly linked list. From the insertion order level, the new entry is inserted at the tail end of the doubly linked list. It is possible to iterate entry according to the order of insertion. In terms of the order of visits, the new put-in entry is the entry of the recent interview. It should also be placed at the tail end of a doubly linked list.

There is also a removeeldestentry method. The method is as follows:

This method is used to write, generally assuming that the linkedhashmap implementation of the LRU algorithm, it is necessary to overwrite the method,//For example, can be overridden to assume that the set of memory is full, then return true, so that when the put//entry again to Linkedhashmap, In the AddEntry method that is called, the least recently used node is removed (the node after the header).

Protected Boolean removeeldestentry (map.entry<k,v> eldest) { return false; }}

This method returns false by default, and we generally overwrite this method when implementing the LRU algorithm with Linkedhashmap. The general implementation is. Returns True when the set memory (this refers to the number of nodes) reaches the maximum value. When you put a new entry (the entry key does not already exist in the hash table). The Removeentryforkey method is called to delete the least recently used node (the node behind the head, which is not actually used recently).


6, Linkedhashmap overwrite the HashMap get method:

Overwrite the Get method in HashMap and get the entry object through the Getentry method. Notice the Recordaccess method here. Suppose that the collation of the elements in the list is sorted according to the order in which they were inserted. This method does nothing,//if the collation of the elements in the list is sorted according to the order of the visits, then E is moved to the end of the 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;    }
Get entry first. Assuming not NULL, the same call to the Recordaccess method, which has been said very clearly, here is not much explanation.

7, finally said Linkedhashmap is how to achieve the LRU.

First of all. When Accessorder is true. The ability to implement the LRU algorithm is enabled by the mode of order of access. We can see that both the Put method and the Get method cause the target entry to become the entry of the recent visit, so the entry is added to the end of the doubly linked list (The Get method is implemented by calling the Recordaccess method. The Put method is implemented by calling the Recordaccess method when overwriting an existing key, and when inserting a new entry, it is implemented by the Addbefore method in Createentry. This puts the recently used entry into the back of the doubly linked list. After several operations, the entry in front of the two-way list is not used recently, so that when the number of nodes is full, the deletion of the front entry (the entry behind head) is the least recently used entry.


  

Anatomy of the "Java Collection source code" LINKEDHASHMAP source code

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.