|
5 | 5 | * Since java doesn't have pointers or random access based on pointer move.
|
6 | 6 | * so we still use HashMap-like design to support quick access, and augment it by another header pointer.
|
7 | 7 | * the header pointers record the element of least accessed and the eldest element.
|
8 |
| - * |
9 |
| - * @Author joy |
10 | 8 | *
|
| 9 | + * @Author joy |
11 | 10 | */
|
12 | 11 | public class LRUCacheImpl extends LRUCache {
|
13 | 12 |
|
14 |
| - transient Entry[] table; |
| 13 | + transient Entry[] table; |
| 14 | + |
| 15 | + int capacity; |
| 16 | + |
| 17 | + int size; |
15 | 18 |
|
16 |
| - int capacity; |
| 19 | + Entry header; |
17 | 20 |
|
18 |
| - int size; |
| 21 | + public LRUCacheImpl(int capacity) { |
| 22 | + super(capacity); |
| 23 | + this.capacity = capacity; |
| 24 | + header = new Entry(-1, -1, -1, null); |
| 25 | + header.after = header.before = header; |
| 26 | + // Find a power of 2 >= initialCapacity |
| 27 | + int capa = 1; |
| 28 | + while (capa < capacity) |
| 29 | + capa <<= 1; |
| 30 | + table = new Entry[capacity]; |
| 31 | + } |
19 | 32 |
|
20 |
| - Entry header; |
| 33 | + /** |
| 34 | + * 1) find the entry with given key,if not found, return; |
| 35 | + * 2) remove the entry out from the doubly linked list(but the not hash bucket,namely next field remains unchanged.); |
| 36 | + * 3) insert it before the header, so that header's "before" field always |
| 37 | + */ |
| 38 | + @Override |
| 39 | + public int get(int key) { |
| 40 | + // 1.find the entry |
| 41 | + int bucketIndx = key % (table.length); |
| 42 | + Entry value = null; |
| 43 | + for (Entry e = table[bucketIndx]; e != null; e = e.next) { |
| 44 | + int k; |
| 45 | + if (e.hash == bucketIndx && ((k = e.key) == key)) { |
| 46 | + value = e; |
| 47 | + break; |
| 48 | + } |
| 49 | + } |
| 50 | + if (value == null) |
| 51 | + return -1; |
| 52 | + // 2. remove the entry from the doubly linked list |
| 53 | + value.after.before = value.before; |
| 54 | + value.before.after = value.after; |
| 55 | + // 3. insert in before the header. |
| 56 | + value.insertBefore(this.header); |
| 57 | + return value.value; |
| 58 | + } |
21 | 59 |
|
22 |
| - public LRUCacheImpl(int capacity) { |
23 |
| - super(capacity); |
24 |
| - this.capacity = capacity; |
25 |
| - header = new Entry(-1, -1, -1, null); |
26 |
| - header.after = header.before = header; |
27 |
| - // Find a power of 2 >= initialCapacity |
28 |
| - int capa = 1; |
29 |
| - while (capa < capacity) |
30 |
| - capa <<= 1; |
31 |
| - table = new Entry[capacity]; |
32 |
| - } |
| 60 | + /** |
| 61 | + * @param key |
| 62 | + * @param value |
| 63 | + */ |
| 64 | + @Override |
| 65 | + public void set(int key, int value) { |
| 66 | + // 1.find the entry |
| 67 | + int bucketIndx = key % (table.length); |
| 68 | + Entry exist = null; |
| 69 | + for (Entry e = table[bucketIndx]; e != null; e = e.next) { |
| 70 | + int k; |
| 71 | + if (e != null && e.hash == bucketIndx && ((k = e.key) == key)) { |
| 72 | + exist = e; |
| 73 | + break; |
| 74 | + } |
| 75 | + } |
| 76 | + // 2. if not exist, insert the entry. |
| 77 | + if (exist == null) { |
| 78 | + Entry newEntry = new Entry(bucketIndx, key, value, |
| 79 | + table[bucketIndx]); |
| 80 | + size++; |
| 81 | + exist = table[bucketIndx] = newEntry; |
| 82 | + } else { |
| 83 | + /* if exist, update the value and remove from the doubly linked list*/ |
| 84 | + exist.value = value; |
| 85 | + exist.before.after = exist.after; |
| 86 | + exist.after.before = exist.before; |
| 87 | + } |
| 88 | + // 3. insert the least accessed entry before the header. |
| 89 | + exist.insertBefore(this.header); |
| 90 | + //4. if size exceeds the capacity,remove the eldest entry. |
| 91 | + if (size > capacity) { |
| 92 | + removeEntry(header.after); |
| 93 | + header.after = header.after.after; |
| 94 | + header.after.before = header; |
| 95 | + size--; |
| 96 | + } |
| 97 | + } |
33 | 98 |
|
34 |
| - /** |
35 |
| - * 1) find the entry with given key,if not found, return; |
36 |
| - * 2) remove the entry out from the doubly linked list(but the not hash bucket,namely next field remains unchanged.); |
37 |
| - * 3) insert it before the header, so that header's "before" field always |
38 |
| - */ |
39 |
| - @Override |
40 |
| - public int get(int key) { |
41 |
| - // 1.find the entry |
42 |
| - int bucketIndx = key % (table.length); |
43 |
| - Entry value = null; |
44 |
| - for (Entry e = table[bucketIndx]; e != null; e = e.next) { |
45 |
| - int k; |
46 |
| - if (e.hash == bucketIndx && ((k = e.key) == key)) { |
47 |
| - value = e; |
48 |
| - break; |
49 |
| - } |
50 |
| - } |
51 |
| - if (value == null) |
52 |
| - return -1; |
53 |
| - // 2. remove the entry from the doubly linked list |
54 |
| - value.after.before = value.before; |
55 |
| - value.before.after = value.after; |
56 |
| - // 3. insert in before the header. |
57 |
| - value.insertBefore(this.header); |
58 |
| - return value.value; |
59 |
| - } |
| 99 | + private void removeEntry(Entry entry) { |
| 100 | + Entry prev = table[entry.hash]; |
| 101 | + Entry e = prev; |
| 102 | + while (e != null) { |
| 103 | + Entry en = e.next; |
| 104 | + if (e.key == entry.key) { |
| 105 | + if (prev == e) |
| 106 | + table[entry.hash] = en ; |
| 107 | + else |
| 108 | + prev.next = e.next; |
| 109 | + break; |
| 110 | + } |
| 111 | + prev = e; |
| 112 | + e = en; |
| 113 | + } |
| 114 | + } |
60 | 115 |
|
61 |
| - /** |
62 |
| - * |
63 |
| - * @param key |
64 |
| - * @param value |
65 |
| - */ |
66 |
| - @Override |
67 |
| - public void set(int key, int value) { |
68 |
| - // 1.find the entry |
69 |
| - int bucketIndx = key % (table.length); |
70 |
| - Entry exist = null; |
71 |
| - for (Entry e = table[bucketIndx]; e != null; e = e.next) { |
72 |
| - int k; |
73 |
| - if (e!=null&& e.hash == bucketIndx && ((k = e.key) == key)) { |
74 |
| - exist = e; |
75 |
| - break; |
76 |
| - } |
77 |
| - } |
78 |
| - // 2. if not exist, insert the entry. |
79 |
| - if (exist == null) { |
80 |
| - Entry newEntry = new Entry(bucketIndx, key, value, |
81 |
| - table[bucketIndx]); |
82 |
| - size++; |
83 |
| - exist = table[bucketIndx] = newEntry; |
84 |
| - } else { |
85 |
| - /* if exist, update the value and remove from the doubly linked list*/ |
86 |
| - exist.value = value; |
87 |
| - exist.before.after = exist.after; |
88 |
| - exist.after.before = exist.before; |
89 |
| - } |
90 |
| - // 3. insert the least accessed entry before the header. |
91 |
| - exist.insertBefore(this.header); |
92 |
| - //4. if size exceeds the capacity,remove the eldest entry. |
93 |
| - if(size>capacity){ |
94 |
| - removeEntry(header.after); |
95 |
| - header.after=header.after.after; |
96 |
| - header.after.before=header; |
97 |
| - size--; |
98 |
| - } |
99 |
| - } |
100 |
| - |
101 |
| - private void removeEntry(Entry entry){ |
102 |
| - Entry prev=table[entry.hash]; |
103 |
| - Entry e=prev; |
104 |
| - while(e!=null){ |
105 |
| - Entry en=e.next; |
106 |
| - if(e.key==entry.key){ |
107 |
| - if(prev==e) |
108 |
| - table[entry.hash]=en; |
109 |
| - else |
110 |
| - prev.next=e.next; |
111 |
| - break; |
112 |
| - } |
113 |
| - prev=e; |
114 |
| - e=en; |
115 |
| - } |
116 |
| - } |
| 116 | + static class Entry { |
| 117 | + int hash; |
| 118 | + int key; |
| 119 | + int value; |
| 120 | + // 和 hashmap一样,每个bucket自身的单链表 |
| 121 | + Entry next;/*pointing to next element in the same hash bucket.*/ |
| 122 | + // 用来做LRU判断的双向循环链表所需要 |
| 123 | + Entry after, before;/*use to record access order*/ |
117 | 124 |
|
118 |
| - static class Entry { |
119 |
| - int hash; |
120 |
| - int key; |
121 |
| - int value; |
122 |
| - Entry next;/*pointing to next element in the same hash bucket.*/ |
123 |
| - Entry after, before;/*use to record access order*/ |
| 125 | + public Entry(int hash, int key, int value, Entry next) { |
| 126 | + this.hash = hash; |
| 127 | + this.key = key; |
| 128 | + this.value = value; |
| 129 | + this.next = next; |
| 130 | + } |
124 | 131 |
|
125 |
| - public Entry(int hash, int key, int value, Entry next) { |
126 |
| - this.hash = hash; |
127 |
| - this.key = key; |
128 |
| - this.value = value; |
129 |
| - this.next = next; |
130 |
| - } |
| 132 | + public Entry(int hash, int key, int value, Entry next, Entry after, |
| 133 | + Entry before) { |
| 134 | + this.hash = hash; |
| 135 | + this.key = key; |
| 136 | + this.value = value; |
| 137 | + this.next = next; |
| 138 | + this.after = after; |
| 139 | + this.before = before; |
| 140 | + } |
131 | 141 |
|
132 |
| - public Entry(int hash, int key, int value, Entry next, Entry after, |
133 |
| - Entry before) { |
134 |
| - this.hash = hash; |
135 |
| - this.key = key; |
136 |
| - this.value = value; |
137 |
| - this.next = next; |
138 |
| - this.after = after; |
139 |
| - this.before = before; |
140 |
| - } |
| 142 | + public void insertBefore(Entry existingEntry) { |
| 143 | + after = existingEntry; |
| 144 | + before = existingEntry.before; |
| 145 | + before.after = this; |
| 146 | + after.before = this; |
| 147 | + } |
141 | 148 |
|
142 |
| - public void insertBefore(Entry existingEntry) { |
143 |
| - after = existingEntry; |
144 |
| - before = existingEntry.before; |
145 |
| - before.after = this; |
146 |
| - after.before = this; |
147 |
| - } |
| 149 | + @Override |
| 150 | + public String toString() { |
| 151 | + return "Entry [hash=" + hash + ", key=" + key + ", value=" + value |
| 152 | + + "]"; |
| 153 | + } |
148 | 154 |
|
149 |
| - @Override |
150 |
| - public String toString() { |
151 |
| - return "Entry [hash=" + hash + ", key=" + key + ", value=" + value |
152 |
| - + "]"; |
153 |
| - } |
154 |
| - |
155 |
| - } |
| 155 | + } |
156 | 156 | }
|
0 commit comments