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 LRUCacheLRUCache(1000);
  9         $this->assertNull($lru->get(1));
 10     }
 11 
 12     public function testGet() {
 13         $lru = new LRUCacheLRUCache(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 LRUCacheLRUCache(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 LRUCacheLRUCache($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 LRUCacheLRUCache($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 LRUCacheLRUCache($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 LRUCacheLRUCache(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 }

以上代码仅供大家参考

原文地址:https://www.cnblogs.com/phpxj/p/11637295.html