LinkedHashMap
In this article, we will look into the internal workings of LinkedHashMap.
LinkedHashMap vs. HashMap
is a that also defines the iteration ordering using an additional data structure, a double linked list. By default, the iteration order is same as insertion-order. It can also be the order in which its entries were last accessed so it can be easily extended to buildData Structure
The data structure of
extends that of .
In HashMap, the data structure is based on array and linked list. An entry finds its location in the array based on its hash value. If an array element is already occupied, the new entry replaces the old entry and the old entry is linked to the new one.
In
, there is no control on the iteration order.
In
, the iteration order is defined, either by the insertion order or access order.![]() |
LinkedHashMap data strutcture |
Below is the
data structure:![]() |
HashMap data structure |
Entry
![]() |
LinkedHashMap Entry class |
New Entry
- The new node’s next reference will point to the head.
- The new node’s previous reference will point to the current last node.
- The current last node’s next reference will point to the new node instead of head.
- Head’s previous reference will point to the new node.
1
2
3
4
| after = head; before = head.before; before.after = this ; after.before = this ; |
![]() |
Double linked-list |
Performance is likely to be just slightly below that of
, due to the added expense of maintaining the linked list.Access Ordered
A special
constructor is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently. Invoking the put or get method results in an access to the corresponding entry. If the enclosing Map is access-ordered, it moves the entry to the end of the list; otherwise, it does nothing.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| public void testLinkedHashMap() { LinkedHashMap lru = new LinkedHashMap( 16 , 0 .75f, true ); lru.put( "one" , null ); lru.put( "two" , null ); lru.put( "three" , null ); Iterator itr = lru.keySet().iterator(); while (itr.hasNext()) { System.out.println(itr.next()); } System.out.println( "** Access one, will move it to end **" ); lru.get( "one" ); itr = lru.keySet().iterator(); while (itr.hasNext()) { System.out.println(itr.next()); } System.out.println( "** Access two, will move it to end **" ); lru.put( "two" , "two" ); itr = lru.keySet().iterator(); while (itr.hasNext()) { System.out.println(itr.next()); } } |
1
2
3
4
5
6
7
8
9
10
11
12
| Result: one two three ** Access one, will move it to end ** two three one ** Access two, will move it to end ** three one two |
Thus in access-ordered linked hash maps, merely querying the map with get is a structural modification.
Iterator
In
In , it simply has to traverse through its own double-linked list thus requires time proportional to the size of the map and not its capacity so iteration is likely to be more expensive.
, the iterator has to traverse through each table element and the element’s own linked list, requiring time proportional to its capacity.In , it simply has to traverse through its own double-linked list thus requires time proportional to the size of the map and not its capacity so iteration is likely to be more expensive.
Transfer
Re-sizing is supposed to be faster as it iterates through its double-linked list to transfer the contents into a new table array.
1
2
3
4
5
6
7
8
| void transfer(HashMap.Entry[] newTable) { int newCapacity = newTable.length; for (Entry e = header.after; e != header; e = e.after) { int index = indexFor(e.hash, newCapacity); e.next = newTable[index]; newTable[index] = e; } } |
Contains Value
LRU Cache
If access ordered is true, order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (access-order). This kind of map is well-suited to building LRU caches. The
method may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map. For example,
1
2
3
| protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxCacheSize; } |
The eldest entry is returned by
. The default implementation of returns false.Access Order
The main reason why one prefers
over is that it can retain the order in which the elements are accessed. Below is the basic flow that a goes through to put a new entry. The blue boxes, ‘Add Entry’ and ‘Record Access’ are the ones overrides.![]() |
LinkedHashMap |
It overrides ‘Record Access’ to record the access order. If the user is interested in the access order, it updates its double linked list.
![]() |
LinkedHashMap |
Below entry picture shows how the entry moves up the linked list. Head’s next entry will point to the latest entry accessed.
![]() |
LinkedHashMap |
If E2 is accessed again, HashMap identifies the entry and then calls record access.
The record access is overridden in LinkedHashMap. Below is the class diagram where LinkedHashMap extends HashMap’s entry to override the recordAccess.
![]() |
LinkedHashMap |
Double linked list vs Single Linked List
![]() |
LinkedHashMap |
Suppose we want to remove an entry E2.
If we have a single linked list, E3’s next should point to E1 which means if want to eliminate E2, we need to know its previous entry. If it is a single linked list, we will end up traversing the entire list to search the entry previous to E2, thus the performance would in the O(n)
In case of double linked list, the previous pointer in E2 will take us to E3 in O(1).
Source: http://javarticles.com/2012/06/linkedhashmap.html
No comments :
Post a Comment