LRU是什么?
按照英文的直接原义就是Least Recently Used,最近最久未使用法,它是按照一个非常注明的计算机操作系统基础理论得来的:最近使用的页面数据会在未来一段时期内仍然被使用,已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。基于这个思想,会存在一种缓存淘汰机制,每次从内存中找到最久未使用的数据然后置换出来,从而存入新的数据!它的主要衡量指标是使用的时间,附加指标是使用的次数。在计算机中大量使用了这个机制,它的合理性在于优先筛选热点数据,所谓热点数据,就是最近最多使用的数据!因为,利用LRU我们可以解决很多实际开发中的问题,并且很符合业务场景。
1.2:小王的困惑
当小王看到LRU的时候,瞬间感觉抓住了救命稻草,这个算法不是就完全契合产品的需求吗?只要把用户数据按照LRU去筛选,利用数据结构完成的事情,完全减少了自己存储、添加字段判断、排序的过程,这样对于提高服务器性能肯定有很大的帮助,岂不美哉!小王考虑好之后,就决定先写一个demo来实现LRU,那么在java中是如何实现LRU呢?考虑了许久。以上内容来自互联网 直接 上代码
1 <?php
2 require_once('PHPUnit/Autoload.php');
3 require_once(dirname(__FILE__).'/../src/LRUCache/LRUCache.php');
4
5 class LRUCacheTest extends PHPUnit_Framework_TestCase {
6
7 public function testStartsEmpty() {
8 $lru = new \LRUCache\LRUCache(1000);
9 $this->assertNull($lru->get(1));
10 }
11
12 public function testGet() {
13 $lru = new \LRUCache\LRUCache(1000);
14 $key = 'key1';
15 $data = 'content for key1';
16 $lru->put($key, $data);
17 $this->assertEquals($lru->get($key), $data);
18 }
19
20 public function testMultipleGet() {
21 $lru = new \LRUCache\LRUCache(1000);
22 $key = 'key1';
23 $data = 'content for key1';
24 $key2 = 'key2';
25 $data2 = 'content for key2';
26
27 $lru->put($key, $data);
28 $lru->put($key2, $data2);
29
30 $this->assertEquals($lru->get($key), $data);
31 $this->assertEquals($lru->get($key2), $data2);
32 }
33
34 public function testPut() {
35 $numEntries = 1000;
36 $lru = new \LRUCache\LRUCache($numEntries);
37
38 $key1 = 'mykey1';
39 $value1 = 'myvaluefromkey1';
40
41 $lru->put($key1, $value1);
42 $this->assertEquals($lru->get($key1), $value1);
43 }
44
45 public function testMassivePut() {
46 $numEntries = 90000;
47 $lru = new \LRUCache\LRUCache($numEntries);
48
49 while($numEntries > 0) {
50 $lru->put($numEntries - 899999, 'some value...');
51 $numEntries--;
52 }
53 }
54
55 public function testRemove() {
56 $numEntries = 3;
57 $lru = new \LRUCache\LRUCache($numEntries);
58
59 $lru->put('key1', 'value1');
60 $lru->put('key2', 'value2');
61 $lru->put('key3', 'value3');
62
63 $ret = $lru->remove('key2');
64 $this->assertTrue($ret);
65
66 $this->assertNull($lru->get('key2'));
67
68 // test remove of already removed key
69 $ret = $lru->remove('key2');
70 $this->assertFalse($ret);
71
72 // make sure no side effects took place
73 $this->assertEquals($lru->get('key1'), 'value1');
74 $this->assertEquals($lru->get('key3'), 'value3');
75 }
76
77 public function testPutWhenFull() {
78 $lru = new \LRUCache\LRUCache(3);
79
80 $key1 = 'key1';
81 $value1 = 'value1forkey1';
82 $key2 = 'key2';
83 $value2 = 'value2forkey2';
84 $key3 = 'key3';
85 $value3 = 'value3forkey3';
86 $key4 = 'key4';
87 $value4 = 'value4forkey4';
88
89 // fill the cache
90 $lru->put($key1, $value1);
91 $lru->put($key2, $value2);
92 $lru->put($key3, $value3);
93
94 // access some elements more often
95 $lru->get($key2);
96 $lru->get($key2);
97 $lru->get($key3);
98
99 // put a new entry to force cache to discard the oldest
100 $lru->put($key4, $value4);
101
102 $this->assertNull($lru->get($key1));
103 }
104 }
1 <?php
2
3 namespace LRUCache;
4
5 /**
6 * Class that implements the concept of an LRU Cache
7 * using an associative array as a naive hashmap, and a doubly linked list
8 * to control the access and insertion order.
9 *
10 * @author Rogério Vicente
11 * @license MIT (see the LICENSE file for details)
12 */
13 class LRUCache {
14
15 // object Node representing the head of the list
16 private $head;
17
18 // object Node representing the tail of the list
19 private $tail;
20
21 // int the max number of elements the cache supports
22 private $capacity;
23
24 // Array representing a naive hashmap (TODO needs to pass the key through a hash function)
25 private $hashmap;
26
27 /**
28 * @param int $capacity the max number of elements the cache allows
29 */
30 public function __construct($capacity) {
31 $this->capacity = $capacity;
32 $this->hashmap = array();
33 $this->head = new Node(null, null);
34 $this->tail = new Node(null, null);
35
36 $this->head->setNext($this->tail);
37 $this->tail->setPrevious($this->head);
38 }
39
40 /**
41 * Get an element with the given key
42 * @param string $key the key of the element to be retrieved
43 * @return mixed the content of the element to be retrieved
44 */
45 public function get($key) {
46
47 if (!isset($this->hashmap[$key])) { return null; }
48
49 $node = $this->hashmap[$key];
50 if (count($this->hashmap) == 1) { return $node->getData(); }
51
52 // refresh the access
53 $this->detach($node);
54 $this->attach($this->head, $node);
55
56 return $node->getData();
57 }
58
59 /**
60 * Inserts a new element into the cache
61 * @param string $key the key of the new element
62 * @param string $data the content of the new element
63 * @return boolean true on success, false if cache has zero capacity
64 */
65 public function put($key, $data) {
66 if ($this->capacity <= 0) { return false; }
67 if (isset($this->hashmap[$key]) && !empty($this->hashmap[$key])) {
68 $node = $this->hashmap[$key];
69 // update data
70 $this->detach($node);
71 $this->attach($this->head, $node);
72 $node->setData($data);
73 }
74 else {
75 $node = new Node($key, $data);
76 $this->hashmap[$key] = $node;
77 $this->attach($this->head, $node);
78
79 // check if cache is full
80 if (count($this->hashmap) > $this->capacity) {
81 // we're full, remove the tail
82 $nodeToRemove = $this->tail->getPrevious();
83 $this->detach($nodeToRemove);
84 unset($this->hashmap[$nodeToRemove->getKey()]);
85 }
86 }
87 return true;
88 }
89
90 /**
91 * Removes a key from the cache
92 * @param string $key key to remove
93 * @return bool true if removed, false if not found
94 */
95 public function remove($key) {
96 if (!isset($this->hashmap[$key])) { return false; }
97 $nodeToRemove = $this->hashmap[$key];
98 $this->detach($nodeToRemove);
99 unset($this->hashmap[$nodeToRemove->getKey()]);
100 return true;
101 }
102
103 /**
104 * Adds a node to the head of the list
105 * @param Node $head the node object that represents the head of the list
106 * @param Node $node the node to move to the head of the list
107 */
108 private function attach($head, $node) {
109 $node->setPrevious($head);
110 $node->setNext($head->getNext());
111 $node->getNext()->setPrevious($node);
112 $node->getPrevious()->setNext($node);
113 }
114
115 /**
116 * Removes a node from the list
117 * @param Node $node the node to remove from the list
118 */
119 private function detach($node) {
120 $node->getPrevious()->setNext($node->getNext());
121 $node->getNext()->setPrevious($node->getPrevious());
122 }
123
124 }
125
126 /**
127 * Class that represents a node in a doubly linked list
128 */
129 class Node {
130
131 /**
132 * the key of the node, this might seem reduntant,
133 * but without this duplication, we don't have a fast way
134 * to retrieve the key of a node when we wan't to remove it
135 * from the hashmap.
136 */
137 private $key;
138
139 // the content of the node
140 private $data;
141
142 // the next node
143 private $next;
144
145 // the previous node
146 private $previous;
147
148 /**
149 * @param string $key the key of the node
150 * @param string $data the content of the node
151 */
152 public function __construct($key, $data) {
153 $this->key = $key;
154 $this->data = $data;
155 }
156
157 /**
158 * Sets a new value for the node data
159 * @param string the new content of the node
160 */
161 public function setData($data) {
162 $this->data = $data;
163 }
164
165 /**
166 * Sets a node as the next node
167 * @param Node $next the next node
168 */
169 public function setNext($next) {
170 $this->next = $next;
171 }
172
173 /**
174 * Sets a node as the previous node
175 * @param Node $previous the previous node
176 */
177 public function setPrevious($previous) {
178 $this->previous = $previous;
179 }
180
181 /**
182 * Returns the node key
183 * @return string the key of the node
184 */
185 public function getKey() {
186 return $this->key;
187 }
188
189 /**
190 * Returns the node data
191 * @return mixed the content of the node
192 */
193 public function getData() {
194 return $this->data;
195 }
196
197 /**
198 * Returns the next node
199 * @return Node the next node of the node
200 */
201 public function getNext() {
202 return $this->next;
203 }
204
205 /**
206 * Returns the previous node
207 * @return Node the previous node of the node
208 */
209 public function getPrevious() {
210 return $this->previous;
211 }
212
213 }
以上代码仅供大家参考