LRU算法简介
LRU是什么?
按照英文的直接原义就是Least Recently Used,最近最久未使用法,它是按照一个非常注明的计算机操作系统基础理论得来的:最近使用的页面数据会在未来一段时期内仍然被使用,已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。基于这个思想,会存在一种缓存淘汰机制,每次从内存中找到最久未使用的数据然后置换出来,从而存入新的数据!它的主要衡量指标是使用的时间,附加指标是使用的次数。在计算机中大量使用了这个机制,它的合理性在于优先筛选热点数据,所谓热点数据,就是最近最多使用的数据!因为,利用LRU我们可以解决很多实际开发中的问题,并且很符合业务场景。
1.2:小王的困惑
当小王看到LRU的时候,瞬间感觉抓住了救命稻草,这个算法不是就完全契合产品的需求吗?只要把用户数据按照LRU去筛选,利用数据结构完成的事情,完全减少了自己存储、添加字段判断、排序的过程,这样对于提高服务器性能肯定有很大的帮助,岂不美哉!小王考虑好之后,就决定先写一个demo来实现LRU,那么在php中是如何实现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 }
以上代码仅供大家参考