Deep understanding of the Java Linkedhashmap Tutorial

Source: Internet
Author: User
Tags constructor hash static class


1.LINKEDHASHMAP Overview:


Linkedhashmap is a subclass of HashMap that retains the order of insertion, and if the order of output needs to be the same as the input, then the Linkedhashmap is selected.

The difference between LINKEDHASHMAP implementations and HashMap is that the latter maintains a doubly linked list running on all entries. This list of links defines the order of the iterations, which can be in the order of insertion or in the order of access.

Note that this implementation is not synchronized. If multiple threads simultaneously access the hash map of a link, and at least one of the threads modifies the mapping from the structure, it must maintain an external synchronization.

Depending on the order of the elements in the list, it can be divided into: a linked list in the order of insertion, and a linked list by Access order (call get method).

The default is to sort by the insertion order, and if you specify the order of access, the call to the Get method will move the elements of this access to the end of the list and continuously access the list that can be sorted by access order. You can override the Removeeldestentry method to return a true value to specify the oldest element to be removed when inserting an element.

2. Implementation of Linkedhashmap:

For Linkedhashmap, it inherits with the HashMap, the bottom uses the hash table and the bidirectional linked list to save all elements. Its basic operation is similar to that of the parent class HashMap, which implements its own linked list attributes by overriding the methods associated with the parent class. Here we will analyze the source code of Linkedhashmap:

Class Structure:

public class linkedhashmap<k, v> extends Hashmap<k, v> implements, Map<k

1) member Variable:

Linkedhashmap uses the same hash algorithm as the HashMap, but it redefined the element entry in the array, which, in addition to holding the reference to the current object, also preserves the last element before and the next element after reference, Thus, a list of two-way links is formed on the basis of a hash table. See Source code:

True indicates that an iteration is in the order of access, false to represent the
private final Boolean accessorder in the insertion order;
/**
* The table header element of a two-way list.  
* *
private transient entry<k,v> header; 
The entry element of the/** * linkedhashmap. 
* Inherits the entry element of HashMap and saves the reference to the last element before and the next element after.  
* *
private static class Entry<k,v> extends Hashmap.entry<k,v> {
entry<k,v> before,  After
...
}

Hashmap.entry:

static class entry<k,v> implements map.entry<k,v> {            final K key;            V value;           Entry<K,V>
 next;           final int hash;              entry (Int h, k k, v v,  entry<k,v> n)  {                value = v;                next = n;                key = k;               hash  = h; &nbsp         }  }



2) Initialization:

It can be seen from the source code that, in the Linkedhashmap construction method, the related construction method of the parent class HashMap is actually called to construct a table array which is stored at the bottom. Such as:

Public Linkedhashmap (int initialcapacity, float loadfactor) {
super (initialcapacity, loadfactor);  
Accessorder = false;
}


The related construction methods in HashMap:

Public hashmap (int initialcapacity, float loadfactor)  {        if  (initialcapacity < 0)            
Throw new illegalargumentexception ("illegal initial capacity: "  +                                                initialcapacity);       if  ( initialcapacity > maximum_capacity)             initialcapacity = maximum_capacity;       if  (loadfactor < = 0 | |  float.isnan (loadfactor))            throw new  IlleGalargumentexception ("illegal load factor: "  +                                                 loadfactor);          // find a power of  2 >= initialCapacity       int capacity = 1;        while  (capacity < initialcapacity)             capacity <<= 1;           this.loadFactor = loadFactor;       threshold =  ( int) (capacity * loadfactor);       table = new entry[ capacity]; &nbsp     init ();  }


We already know that Linkedhashmap's entry element inherits HashMap's entry and provides a two-way list of functions. In the above HashMap constructor, the init () method is finally invoked to perform the associated initialization, which is meaningless in the implementation of the HashMap, but provides an initialization call to the subclass implementation.
Linkedhashmap rewrites the Init () method, and further implements the initialization of its element entry after invoking the constructor method of the parent class 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 Put method of the parent class HashMap, but instead overrides the child method void Recordaccess (HashMap m) of the method call to the parent class HashMap, void addentry (int hash, K Key, V value, int bucketindex) and void createentry (int hash, K key, V value, int bucketindex) provide their own implementation of a unique two-way link list.

Hashmap.put:

Public v put (k key, v value)  {            if  (key == null)                 return putfornullkey (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   (e.hash == hash &&  (k = e.key)  == key | |  key.equals (k))  {   &Nbsp;               v oldvalue  = e.value;                    e.value = value;                    e.recordaccess (this);       
            return oldValue;               }            }              modCount++;            addentry (hash, key, value, i);            return null;        }


Override method:

void Recordaccess (hashmap<k,v> m) {
linkedhashmap<k,v> lm = (linkedhashmap<k,v>) m;  
if (lm.accessorder) {
lm.modcount++;  
Remove ();  
Addbefore (Lm.header);        
}  
}
Void addentry (Int hash, k key, v value, int bucketindex)  {        //  calls the Create method to add the new element to the map as a two-way linked list.
       createentry (Hash, key, value, bucketindex);          //  Remove policy definitions for least recently used elements        entry<k,v > eldest = header.after;       if  (Removeeldestentry ( eldest))  {           removeentryforkey (Eldest.key);        } else {            if  (size >= threshold)                 resize (2 * table.length);       }  }    Void createentry (Int hash, k keY, v value, int bucketindex)  {       hashmap.entry<k, v> old = table[bucketindex];       entry<k,v> e  = new Entry<K,V> (hash, key, value, old);        table[bucketIndex] = e;       //  invokes the Addbrefore method of the element, adding the element to the hash, A two-way link list.        e.addbefore (header);       size++; 
 }   Private void addbefore (entry<k,v> existingentry)  {       after  = existingEntry;       before 
= existingentry.before;       before.after = this;       after.before = this;  }


  &NBSP
4 reads:

Linkedhashmap overrides the Get method of the parent class HashMap, actually after calling the parent class Getentry () method to obtain the lookup element, Again, when the sort mode accessorder to true, record access order, add the most recently accessed elements to the header of the bidirectional linked list, and remove from the original location. Because of the increase in the list, delete operations are constant-level, it does not cause loss of performance.

Hashmap.containsvalue:

Public boolean containsvalue (Object value)  {       if  ( Value == null)                 Return containsnullvalue ();          Entry[] tab =  table;           for  (int i = 0; i  < tab.length ; i++)                 for  (entry e = tab[i] ; e != null ; e  = e.next)                     if  (Value.equals (e.value))                         return true;        return false;       }    /* Find out if the map contains a given value, or consider the Linkedhashmap has a double linked list, where override is designed to improve the efficiency of iterations.    */   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;   &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;}  else {               for 
(Entry e = header.after; e != header; e = e.after)                    if  ( Value.equals (e.value))                         return true;        
   }           return false;       }
/* The transfer () is an implementation in HashMap: Iterate through the buckets of the entire table, then iterate over the bucket to get each entry, and hash to newtable,   // It's here for the Linkedhashmap. Rewrite the comparison of the method,   void transfer (entry[] newtable)  {           Entry[] src = table;           int newCapacity = newTable.length;           for  (int j = 0; j < src.length; j++)  {               Entry<K,V> e = src[j];               if  (E != null)  {                   src[j] = null;                   do {  &Nbsp;                    Entry<K,V> next = e.next;                       int i = indexfor ( e.hash, newcapacity);                       e.next = newTable[i];                       newtable[i] =  e;                       e = next;                   } while  (e != null);               }          }      The  }   */       /**   *transfer () method is the method that is invoked when the parent class HashMap calls resize (). It is the function of table expansion, the old table to hash the key to the new table.    * This method from the parent class HashMap is written because the Linkedhashmap has a double linked list, where override is designed to increase the efficiency of the iteration.    */    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;      }    }   public  v&Nbsp;get (Object key)  {       //  invokes the Getentry () method of the parent class HashMap to obtain the element to find.        Entry<K,V> e =  (entry<k,v>) getentry (key);        if  (e == null)             return null;       //  record Access order.        e.recordaccess (this);       return  e.value;  }   void recordaccess (hashmap<k,v> m)  {        LinkedHashMap<K,V> lm =  (linkedhashmap<k,v>) m;        //  Delete elements from previous positions if the Linkedhashmap iteration order is defined as the access order,       //  And adds the most recently accessed elements to the list header.        if  (lm.accessorder)  {            lm.modcount++;           remove ();            addbefore (lm.header);       }  }   /**           * Removes this entry 
from the linked list.           */           private void remove ()  {                before.after = after;    
           after.before = before;           }  /**clear linked list, set header to initial state */   public  void clear ()  {    super.clear ();    header.before =  Header.after = header;  } 



5) Sorting mode:

Linkedhashmap defines the sort mode accessorder, which is a Boolean variable, true for the access order, or FALSE for the insertion order.

Private Final Boolean Accessorder;

In general, you do not have to specify a sort mode, whose iteration order is the default insertion order. Look at the construction method of Linkedhashmap, such as:

Public Linkedhashmap (int initialcapacity, float loadfactor) {
super (initialcapacity, loadfactor);  
Accessorder = false;
}


These constructors specify the sort mode as the insertion order by default. If you want to construct a linkedhashmap, and you intend to save elements in the order in which they are most frequently accessed from recent visits, that is, access order, use the following construction method to construct 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, and this mapping is ideal for building the LRU cache. Linkedhashmap provides a removeeldestentry (map.entry<k,v> eldest) method. This method can provide an implementation that removes the oldest entry every time a new entry is added, false by default, so that the behavior of this mapping is similar to the normal mapping, that is, never removing the oldest element.


When a new element is added to the map, the entry AddEntry method is invoked, and the Removeeldestentry method is invoked, which is where the LRU element expiration mechanism is implemented, By default, the Removeeldestentry method returns only false to indicate that the element never expires.

 /**      * This override alters behavior of  superclass put method. it causes newly      * allocated  entry to get inserted at the end of the linked list  and      * removes the eldest entry if appropriate .      */      void addentry (int hash, k  Key, v value, int bucketindex)  {           createentry (hash, key, value, bucketindex);              // remove eldest entry if instructed, else grow  capacity if appropriate          Entry<K,V>  eldest = header.after;          if  (Removeeldestentry (eldest))  {               removeentryforkey (Eldest.key);           } else {               if  (size >= threshold)                     resize (2 *  table.length);          }      }          /**      * this override differs &NBSP;FROM&NBSP;ADDENTRY&NBSP;IN&NBSP;THAT&NBSP;IT&NBSP;DOESN ' t resize the       * table or remove the eldest entry.      */    &NBSP;&NBSP;&NBSP;void createentry (Int hash, k key, v value, int bucketindex)  {           hashmap.entry<k,v> old = table[ bucketindex];   entry<k,v> e = new entry<k,v> (hash, key,  Value, old);          table[bucketIndex] = e;           e.addbefore (header);           size++;      }         protected  Boolean removeeldestentry (map.entry<k,v> eldest)  {           return false;      }


This method usually does not modify the mappings in any way, but instead allows the mappings to be modified under the guidance of their return values. If you build the LRU cache with this mapping, it is convenient to allow mappings to reduce memory loss by deleting old entries.

For example, override this method to maintain this mapping to save only the stable state of 100 entries, and delete the oldest entries each time a new entry is added.

private static final int max_entries = 100;  
protected Boolean removeeldestentry (Map.entry eldest) {return
size () > max_entries;
}



the difference between HashMap and Linkedhashmap
 
In general, the most we use is the HASHMAP, which inserts, deletes, and locates elements in the map, and HashMap is the best choice. But if you want to iterate through the keys in natural or custom order, then TreeMap will be better. If the order of output needs to be the same as the input, it can be implemented with LINKEDHASHMAP, which can also be arranged in the order of reading. The

HashMap is the most commonly used map that stores data based on the key's Hashcode value, which can be accessed directly by the key, with a fast access speed. HashMap allows only one record key to be null, allowing multiple records to have a value of NULL.
HashMap does not support thread synchronization, where multiple threads can write HashMap at the same time, which can result in inconsistencies in data. If you need to sync, you can use the collections Synchronizedmap method to make HashMap have the ability to synchronize. The

Hashtable is similar to HashMap, except that it does not allow a record key or value to be null; it supports thread synchronization, in which only one thread can write hashtable at any one time, and therefore Hashtable is slower to write. The

Linkedhashmap saves the insertion order of the records, and the resulting records must be inserted first when traversing linkedhashmap with iterator.

can be traversed more slowly than hashmap slow treemap the records it saves are sorted according to the key, by default, in ascending order, or by specifying the sort comparer. When traversing treemap with iterator, the resulting records are ordered.


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.