LRU缓存设计

use*_*850 66 c++ algorithm lru data-structures

最近最少使用(LRU)缓存首先丢弃最近最少使用的项目如何设计和实现这样的缓存类?设计要求如下:

1)尽可能快地找到项目

2)一旦缓存未命中并且缓存已满,我们需要尽快替换最近最少使用的项目.

如何在设计模式和算法设计方面分析和实现这个问题?

小智 97

链表+链表节点指针的哈希表是实现LRU缓存的常用方法.这给出了O(1)操作(假设一个不错的哈希值).这样做的好处(是O(1)):你可以通过锁定整个结构来做多线程版本.您不必担心粒状锁定等.

简而言之,它的工作方式:

在访问值时,将链接列表中的相应节点移动到头部.

当您需要从缓存中删除值时,您将从尾端删除.

向缓存添加值时,只需将其放在链表的头部即可.

感谢doublep,这里是带有C++实现的站点:杂项容器模板.

  • @Moron:我会使用双向链表. (4认同)
  • @Rafal:为什么问题复杂化?给定双链表实现和哈希表实现,您可以轻松地将它们放在一起以创建LRU实现.尝试在哈希表中维护链表结构将使您自己实现链表方法,并且您将无法使用现成的方法... (3认同)
  • @darid:我认为你也可以使用单链表,并使哈希表指向包含哈希值的*previous*节点. (2认同)
  • 顺便说一下,[Miscellaneous Container Templates](https://launchpad.net/libmct) 中有一个 C++ 的链接哈希表实现。它的文档包含完全适合此用例的示例。 (2认同)
  • java中还有一个具有LRU选项的链接哈希图实现:[LinkedHashMap](http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html) (2认同)

Tsu*_*oka 23

这是我对LRU缓存的简单示例c ++实现,结合了hash(unordered_map)和list.列表中的项目具有访问映射的键,而映射上的项目具有列表访问列表的迭代器.

#include <list>
#include <unordered_map>
#include <assert.h>

using namespace std;

template <class KEY_T, class VAL_T> class LRUCache{
private:
        list< pair<KEY_T,VAL_T> > item_list;
        unordered_map<KEY_T, decltype(item_list.begin()) > item_map;
        size_t cache_size;
private:
        void clean(void){
                while(item_map.size()>cache_size){
                        auto last_it = item_list.end(); last_it --;
                        item_map.erase(last_it->first);
                        item_list.pop_back();
                }
        };
public:
        LRUCache(int cache_size_):cache_size(cache_size_){
                ;
        };

        void put(const KEY_T &key, const VAL_T &val){
                auto it = item_map.find(key);
                if(it != item_map.end()){
                        item_list.erase(it->second);
                        item_map.erase(it);
                }
                item_list.push_front(make_pair(key,val));
                item_map.insert(make_pair(key, item_list.begin()));
                clean();
        };
        bool exist(const KEY_T &key){
                return (item_map.count(key)>0);
        };
        VAL_T get(const KEY_T &key){
                assert(exist(key));
                auto it = item_map.find(key);
                item_list.splice(item_list.begin(), item_list, it->second);
                return it->second->second;
        };

};
Run Code Online (Sandbox Code Playgroud)

  • list在内部实现为双向链表,unordered_map基本上是一个哈希表.所以这在时间和空间复杂性方面是有效的解决方案. (6认同)
  • 你为什么使用列表和unordeed地图? (2认同)

And*_*der 12

我在这里看到了一些不必要的复杂实现,因此我决定也提供我的实现。缓存只有两个方法,get和set。希望它具有更好的可读性和理解性:

#include<unordered_map>
#include<list>

using namespace std;

template<typename K, typename V = K>
class LRUCache
{

private:
    list<K>items;
    unordered_map <K, pair<V, typename list<K>::iterator>> keyValuesMap;
    int csize;

public:
    LRUCache(int s) :csize(s) {
        if (csize < 1)
            csize = 10;
    }

    void set(const K key, const V value) {
        auto pos = keyValuesMap.find(key);
        if (pos == keyValuesMap.end()) {
            items.push_front(key);
            keyValuesMap[key] = { value, items.begin() };
            if (keyValuesMap.size() > csize) {
                keyValuesMap.erase(items.back());
                items.pop_back();
            }
        }
        else {
            items.erase(pos->second.second);
            items.push_front(key);
            keyValuesMap[key] = { value, items.begin() };
        }
    }

    bool get(const K key, V &value) {
        auto pos = keyValuesMap.find(key);
        if (pos == keyValuesMap.end())
            return false;
        items.erase(pos->second.second);
        items.push_front(key);
        keyValuesMap[key] = { pos->second.first, items.begin() };
        value = pos->second.first;
        return true;
    }
};
Run Code Online (Sandbox Code Playgroud)