|
1 | 1 | package hashtable.design;
|
2 | 2 |
|
| 3 | +import static org.junit.Assert.assertEquals; |
| 4 | + |
3 | 5 | import java.util.HashMap;
|
4 | 6 | import java.util.Map;
|
5 | 7 |
|
6 |
| -class ListNode |
| 8 | +import org.junit.Test; |
| 9 | + |
| 10 | +class DLLNode |
7 | 11 | {
|
8 |
| - public int key; |
| 12 | + public int key; |
9 | 13 | public int value;
|
10 |
| - public ListNode prev; |
11 |
| - public ListNode next; |
12 |
| - public ListNode( int key, int value ) |
| 14 | + public DLLNode prev; |
| 15 | + public DLLNode next; |
| 16 | + public DLLNode( int key, int value ) |
13 | 17 | {
|
14 |
| - this.key = key; |
| 18 | + this.key = key; |
15 | 19 | this.value = value;
|
16 |
| - this.prev = prev; |
17 |
| - this.next = next; |
18 | 20 | }
|
19 | 21 | }
|
20 | 22 |
|
21 | 23 | public class LRUCacheHashTableDDL
|
22 | 24 | {
|
23 |
| - private Map<Integer, ListNode> keyToNodeMap; |
| 25 | + |
| 26 | + private Map<Integer, DLLNode> cache; |
24 | 27 | // list ordered from oldest to latest
|
25 |
| - private ListNode listHeadSentinel; |
26 |
| - private ListNode listTailSentinel; |
| 28 | + private DLLNode dummyOldest; |
| 29 | + private DLLNode dummyLatest; |
27 | 30 | private int capacity;
|
28 |
| - private final static int SENTINEL = 0; |
29 | 31 |
|
30 |
| - public LRUCacheHashTableDDL(int capacity) |
| 32 | + public LRUCacheHashTableDDL( int capacity ) |
31 | 33 | {
|
32 | 34 | this.capacity = capacity;
|
33 |
| - this.keyToNodeMap = new HashMap<>( capacity ); |
34 |
| - listHeadSentinel = new ListNode( SENTINEL, SENTINEL ); |
35 |
| - listTailSentinel = new ListNode( SENTINEL, SENTINEL ); |
36 |
| - listHeadSentinel.next = listTailSentinel; |
37 |
| - listTailSentinel.prev = listHeadSentinel; |
| 35 | + this.cache = new HashMap<>( capacity ); |
| 36 | + dummyOldest = new DLLNode( 0, 0 ); |
| 37 | + dummyLatest = new DLLNode( 0, 0 ); |
| 38 | + dummyLatest.prev = dummyOldest; |
| 39 | + dummyOldest.next = dummyLatest; |
38 | 40 | }
|
39 | 41 |
|
40 |
| - public int get(int key) |
| 42 | + public int get( int key ) |
41 | 43 | {
|
42 |
| - if ( keyToNodeMap.containsKey( key ) ) |
| 44 | + if ( !cache.containsKey( key ) ) |
43 | 45 | {
|
44 |
| - ListNode node = keyToNodeMap.get( key ); |
| 46 | + return -1; |
| 47 | + } |
| 48 | + |
| 49 | + DLLNode node = cache.get( key ); |
45 | 50 |
|
46 |
| - // move node to list tail |
47 |
| - removeNodeFromList( node ); |
48 |
| - addNodeToListTail( node ); |
| 51 | + // move node to list tail |
| 52 | + deleteNode( node ); |
| 53 | + addAsLatest( node ); |
49 | 54 |
|
50 |
| - if ( node != null ) |
51 |
| - { |
52 |
| - return node.value; |
53 |
| - } |
54 |
| - else |
55 |
| - { |
56 |
| - throw new IllegalStateException(""); |
57 |
| - } |
58 |
| - } |
59 |
| - else |
60 |
| - { |
61 |
| - return -1; |
62 |
| - } |
| 55 | + return node.value; |
63 | 56 | }
|
64 | 57 |
|
65 |
| - public void set(int key, int value) |
| 58 | + public void put( int key, int value ) |
66 | 59 | {
|
67 | 60 | // judge and pop up oldest entry when necessary
|
68 |
| - if ( !keyToNodeMap.containsKey( key ) ) |
| 61 | + if ( !cache.containsKey( key ) |
| 62 | + && cache.size() == capacity ) |
69 | 63 | {
|
70 |
| - if ( keyToNodeMap.size() == capacity ) |
71 |
| - { |
72 |
| - ListNode cacheToBeRemoved = listHeadSentinel.next; |
73 |
| - removeNodeFromList( cacheToBeRemoved ); |
74 |
| - keyToNodeMap.remove( cacheToBeRemoved.key ); |
75 |
| - } |
| 64 | + DLLNode oldest = dummyOldest.next; |
| 65 | + deleteNode( oldest ); |
| 66 | + cache.remove( oldest.key ); |
76 | 67 | }
|
77 | 68 |
|
78 |
| - if ( keyToNodeMap.containsKey( key ) ) |
| 69 | + if ( cache.containsKey( key ) ) |
79 | 70 | {
|
80 |
| - ListNode existingNode = keyToNodeMap.get( key ); |
81 |
| - removeNodeFromList( existingNode ); |
| 71 | + DLLNode existingNode = cache.get( key ); |
82 | 72 | existingNode.value = value;
|
83 |
| - addNodeToListTail( existingNode ); |
| 73 | + |
| 74 | + deleteNode( existingNode ); |
| 75 | + addAsLatest( existingNode ); |
84 | 76 | }
|
85 | 77 | else
|
86 | 78 | {
|
87 |
| - ListNode node = new ListNode( key, value ); |
88 |
| - addNodeToListTail( node ); |
89 |
| - keyToNodeMap.put( key, node ); |
| 79 | + DLLNode node = new DLLNode( key, value ); |
| 80 | + cache.put( key, node ); |
| 81 | + addAsLatest( node ); |
90 | 82 | }
|
91 | 83 | }
|
92 | 84 |
|
93 |
| - private void addNodeToListTail( ListNode nodeToBeAdded ) |
| 85 | + private void addAsLatest( DLLNode node ) |
94 | 86 | {
|
95 |
| - ListNode nodeBeforeTail = listTailSentinel.prev; |
| 87 | + DLLNode beforeTail = dummyLatest.prev; |
96 | 88 |
|
97 |
| - nodeBeforeTail.next = nodeToBeAdded; |
98 |
| - nodeToBeAdded.prev = nodeBeforeTail; |
| 89 | + beforeTail.next = node; |
| 90 | + node.prev = beforeTail; |
99 | 91 |
|
100 |
| - nodeToBeAdded.next = listTailSentinel; |
101 |
| - listTailSentinel.prev = nodeToBeAdded; |
| 92 | + node.next = dummyLatest; |
| 93 | + dummyLatest.prev = node; |
| 94 | + } |
| 95 | + |
| 96 | + private void deleteNode( DLLNode node ) |
| 97 | + { |
| 98 | + DLLNode beforeNode = node.prev; |
| 99 | + DLLNode afterNode = node.next; |
| 100 | + beforeNode.next = afterNode; |
| 101 | + afterNode.prev = beforeNode; |
102 | 102 | }
|
103 | 103 |
|
104 |
| - private void removeNodeFromList( ListNode nodeToBeRemoved ) |
| 104 | + public static void main( String[] args ) |
105 | 105 | {
|
106 |
| - ListNode nodeBeforeCurr = nodeToBeRemoved.prev; |
107 |
| - ListNode nodeAfterCurr = nodeToBeRemoved.next; |
108 |
| - nodeBeforeCurr.next = nodeAfterCurr; |
109 |
| - nodeAfterCurr.prev = nodeBeforeCurr; |
| 106 | + LRUCacheHashTableDDL table = new LRUCacheHashTableDDL( 2 ); |
| 107 | + table.put( 1, 1 ); |
| 108 | + table.put( 2, 2 ); |
| 109 | + assertEquals( 1, table.get( 1 ) ); |
| 110 | + table.put( 3, 3 ); |
| 111 | + assertEquals( -1, table.get( 2 )); |
110 | 112 | }
|
111 | 113 | }
|
0 commit comments