Schematic set 6: LinkedHashMap, schematic linkedhashmap

Source: Internet
Author: User

Schematic set 6: LinkedHashMap, schematic linkedhashmap

First knowledge of LinkedHashMap

The last two articles talked about the problems caused by HashMap and HashMap in multiple threads, and explained that HashMap is a very common and useful set, in addition, improper use of multiple threads may cause thread security problems.

In most cases, as long as thread security issues are not involved, Map can basically use HashMap. However, there is a problem with HashMap:The order of iterative HashMap is not the order where HashMap is placed, That is, disorder. This disadvantage of HashMap is often troublesome, because in some scenarios, we look forward to an orderly Map.

At this time, LinkedHashMap was launched. Although it increases the time and space overheadBy maintaining a two-way linked list running on all entries, LinkedHashMap ensures the order of element iterations..

 

Answers with four focus on LinkedHashMap

Close note Conclusion
Whether LinkedHashMap can be empty Both Key and Value can be empty.
Whether repeated data is allowed in LinkedHashMap Duplicate Key will overwrite, and repeated Value will be allowed
Sequence hashmap Ordered
Whether LinkedHashMap is thread-safe Non-thread security

 

LinkedHashMap Basic Structure

For LinkedHashMap, we should first mention two points:

1. LinkedHashMap can be consideredHashMap + upload listThat is, it uses HashMap to operate the data structure, and uses the sorted list to maintain the order of inserted elements.

2. The basic implementation idea of LinkedHashMap is ----Polymorphism. It can be said that understanding polymorphism and understanding the principle of LinkedHashMap will get twice the result with half the effort. On the contrary, learning the principle of LinkedHashMap can also promote and deepen the understanding of polymorphism.

For more information, see the definition of LinkedHashMap:

public class LinkedHashMap<K,V>    extends HashMap<K,V>    implements Map<K,V>{    ...}

As you can see, LinkedHashMap is a subclass of HashMap. Naturally, LinkedHashMap inherits all non-private methods in HashMap. Let's take a look at the methods in LinkedHashMap:

There are no methods for operating the data structure in LinkedHashMap. That is to say, the method for operating the data structure (such as put a data) in LinkedHashMap is exactly the same as that for operating the data in HashMap, there are some differences in details.

The difference between LinkedHashMap and HashMap lies in their basic data structure. Let's take a look at the basic data structure of LinkedHashMap, that is, Entry:

private static class Entry<K,V> extends HashMap.Entry<K,V> {    // These fields comprise the doubly linked list used for iteration.    Entry<K,V> before, after;Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {        super(hash, key, value, next);    }    ...}

List some attributes in the Entry:

1. K key

2. V value

3. Entry <K, V> next

4. int hash

5. Entry <K, V> before

6. Entry <K, V> after

The first four, that is, the red part, are inherited from HashMap. Entry; the other two, that is, the blue part, are unique to LinkedHashMap. Do not mistaken next, before, and After,Next is used to maintain the order of entries connected to the specified table location of HashMap. before and After are used to maintain the Entry insertion sequence..

You can also use a diagram to indicate the attributes:

 

Initialize LinkedHashMap

Suppose there is such a piece of code:

1 public static void main(String[] args)2 {3     LinkedHashMap<String, String> linkedHashMap =4             new LinkedHashMap<String, String>();5     linkedHashMap.put("111", "111");6     linkedHashMap.put("222", "222");7 }

The first is 3rd rows ~ Line 3: A new LinkedHashMap is shown. Let's take a look at what we have done:

 1 public LinkedHashMap() { 2 super(); 3     accessOrder = false; 4 }
1 public HashMap() {2     this.loadFactor = DEFAULT_LOAD_FACTOR;3     threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);4     table = new Entry[DEFAULT_INITIAL_CAPACITY];5     init();6 }
 1 void init() { 2     header = new Entry<K,V>(-1, null, null, null); 3     header.before = header.after = header; 4 }
/** * The head of the doubly linked list. */private transient Entry<K,V> header;

Here we see the first polymorphism: init () method. Although the init () method is defined in HashMap:

1. LinkedHashMap overwrites the init method.

2. the instantiated is LinkedHashMap.

Therefore, the init method actually called is the init method rewritten by LinkedHashMap. Assuming that the header address is 0x00000000, the initialization is completed as follows:

 

LinkedHashMap add elements

Let's continue to see what the javashashmap adds elements, that is, what put ("111", "111") does. First of all, it is to call the put Method of HashMap:

 1 public V put(K key, V value) { 2     if (key == null) 3         return putForNullKey(value); 4     int hash = hash(key.hashCode()); 5     int i = indexFor(hash, table.length); 6     for (Entry<K,V> e = table[i]; e != null; e = e.next) { 7         Object k; 8         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 9             V oldValue = e.value;10             e.value = value;11             e.recordAccess(this);12             return oldValue;13         }14     }15 16     modCount++;17     addEntry(hash, key, value, i);18     return null;19 }

Row 17th is also a polymorphism. Because LinkedHashMap overwrites the addEntry method, addEntry calls the method rewritten by LinkedHashMap:

 1 void addEntry(int hash, K key, V value, int bucketIndex) { 2     createEntry(hash, key, value, bucketIndex); 3  4     // Remove eldest entry if instructed, else grow capacity if appropriate 5     Entry<K,V> eldest = header.after; 6     if (removeEldestEntry(eldest)) { 7         removeEntryForKey(eldest.key); 8     } else { 9         if (size >= threshold)10             resize(2 * table.length);11     }12 }

Because LinkedHashMap maintains the insertion sequence, LinkedHashMap can be used for caching, 5th rows ~ The first row is used to support the FIFO algorithm. You do not need to care about it for the time being. Let's take a look at the createEntry method:

1 void createEntry(int hash, K key, V value, int bucketIndex) {2     HashMap.Entry<K,V> old = table[bucketIndex];3     Entry<K,V> e = new Entry<K,V>(hash, key, value, old);4     table[bucketIndex] = e;5     e.addBefore(header);6     size++;7 }
private void addBefore(Entry<K,V> existingEntry) {    after  = existingEntry;    before = existingEntry.before;    before.after = this;    after.before = this;}

2nd rows ~ There is no difference between the Code in line 1 and HashMap. The newly added elements are placed on table [I]. The difference is that LinkedHashMap also performs the addBefore operation, the four lines of code mean to generate a two-way linked list for the new Entry and the original linked list. Assume that string 111 is placed on the position table [1] and the generated Entry address is 0x00000001. The figure shows this:

If you are familiar with the source code of the listing list, it is not difficult to understand, or explain it. Note that the existingEntry indicates the header:

1. after = existingEntry, that is, the after = header address of the newly added Entry, that is, after = 0x00000000

2. before = existingEntry. before: The before of the newly added Entry is the before address of the header. The before of the header is 0x00000000. Therefore, the before of the newly added Entry is 0x00000000.

3. before. after = this: before of the newly added Entry is 0x00000000, that is, header, after = this of header, that is, after = 0x00000001 of header

4. after. before = this: after the newly added Entry, 0x00000000 indicates the header, and before = this indicates the header's before = 0x00000000.

In this way, a two-way linked list is formed between the header and the newly added Entry. Then, what is the new string after 222? Assume that the address of the newly added Entry is 0x00000002 and is generated on table [2], which is shown in the figure below:

As long as before and after are cleared to know which Entry represents, there is no problem.

Let's take a look at it. Again, the implementation of LinkedHashMap is the implementation of HashMap + LinkedList. The data structure is maintained by HashMap and the data insertion sequence is maintained by LinkList.

 

Implement LRU algorithm cache using LinkedHashMap

As mentioned above, LinkedHashMap adds elements. It is relatively simple to delete and modify elements. It is similar to deleting and modifying elements in HashMap + LinkedList. Next we will introduce a new content.

LinkedHashMap can be used as a cache, for example, LRUCache. Let's take a look at the code of this class. It's just a dozen lines:

public class LRUCache extends LinkedHashMap{    public LRUCache(int maxSize)    {        super(maxSize, 0.75F, true);        maxElements = maxSize;    }    protected boolean removeEldestEntry(java.util.Map.Entry eldest)    {        return size() > maxElements;    }    private static final long serialVersionUID = 1L;    protected int maxElements;}

As the name suggests, LRUCache is a Cache based on the LRU algorithm. This class inherits from LinkedHashMap, and there is no special method in the class, this indicates that LRUCache implements the cache LRU function from LinkedHashMap. LinkedHashMap can implement the LRU algorithm cache based on two points:

1. Sort list first it is a Map, Map is based on K-V, consistent with the cache

2. The sorted list provides a boolean value that allows you to specify whether to implement LRU.

First, let's take a look at what is LRU:LRU is Least Recently Used, which is Least Recently Used. That is to say, when the cache is full, the Least Recently accessed data will be preferentially eliminated.. For example, data a is accessed one day ago. Data B is accessed two days ago. If the cache is full, data B is eliminated first.

Let's take a look at the constructor with boolean parameters in the shortlist:

public LinkedHashMap(int initialCapacity,         float loadFactor,                     boolean accessOrder) {    super(initialCapacity, loadFactor);    this.accessOrder = accessOrder;}

This is the accessOrder, which indicates:

(1) false. All entries are arranged in the insert order.

(2) true. All entries are arranged in the access order.

The second point means that if there are three entries 1 2 3, 1 is accessed, and 1 is moved to the end, that is, 2 3 1. When the accessed data is moved to the end of the two-way queue during each access, isn't the data at the end of the two-way queue the data that is least frequently accessed? In other words, the data at the end of the two-way linked list is the data to be eliminated.

"Access", which has two meanings:

1. get the Value based on the Key, that is, the get method.

2. Modify the Value corresponding to the Key, that is, the put method.

First, let's take a look at the get method, which is overwritten in LinkedHashMap:

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;}

Then the put method follows the HashMap of the parent class:

 1 public V put(K key, V value) { 2     if (key == null) 3         return putForNullKey(value); 4     int hash = hash(key.hashCode()); 5     int i = indexFor(hash, table.length); 6     for (Entry<K,V> e = table[i]; e != null; e = e.next) { 7         Object k; 8         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 9             V oldValue = e.value;10             e.value = value;11             e.recordAccess(this);12             return oldValue;13         }14     }15 16     modCount++;17     addEntry(hash, key, value, i);18     return null;19 }

Modify data that is 6th rows ~ 14th lines of code. The code at both ends has one thing in common:The recordAccess method is called.And this method is the method in the Entry, that is, every recordAccess operation is a fixed Entry.

RecordAccess, as its name implies, records access. That is to say, if you access a two-way linked list this time, I will record you. How can I record it?Move the Entry you accessed to the end. This method is an empty method in HashMap, which is used to access sub-class records. Let's take a look at the implementation in LinkedHashMap:

void recordAccess(HashMap<K,V> m) {    LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;    if (lm.accessOrder) {        lm.modCount++;        remove();        addBefore(lm.header);    }}
private void remove() {    before.after = after;    after.before = before;}
private void addBefore(Entry<K,V> existingEntry) {    after  = existingEntry;    before = existingEntry.before;    before.after = this;    after.before = this;}

We can see two things are done each time recordAccess:

1. Connect the front and back Entry of the Entry to be moved

2. Move the Entry to the end

Of course, this is all based on accessOrder = true. The last figure shows the entire recordAccess process:

 

Code demonstrate the effect of sorted by access order of LinkedHashMap

The final Code demonstrates the effect of sorted sort list by access order, and verifies the LRU function of the previous segment of LinkedHashMap:

public static void main(String[] args){    LinkedHashMap<String, String> linkedHashMap =            new LinkedHashMap<String, String>(16, 0.75f, true);    linkedHashMap.put("111", "111");    linkedHashMap.put("222", "222");    linkedHashMap.put("333", "333");    linkedHashMap.put("444", "444");    loopLinkedHashMap(linkedHashMap);    linkedHashMap.get("111");    loopLinkedHashMap(linkedHashMap);    linkedHashMap.put("222", "2222");    loopLinkedHashMap(linkedHashMap);}    public static void loopLinkedHashMap(LinkedHashMap<String, String> linkedHashMap){    Set<Map.Entry<String, String>> set = inkedHashMap.entrySet();    Iterator<Map.Entry<String, String>> iterator = set.iterator();        while (iterator.hasNext())    {        System.out.print(iterator.next() + "\t");    }    System.out.println();}

Note that the constructor here uses the three parameters and the final value must be set to true, which indicates sorting by access order. Let's take a look at the code running result:

111=111    222=222    333=333    444=444    222=222    333=333    444=444    111=111    333=333    444=444    111=111    222=2222   

The code running result proves two points:

1. The sorted list is ordered.

2. Each time an element (get or put) is accessed, the accessed element is mentioned at the end.

 

Related Article

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.