使用std::map和std::list實現LRU(…

     最近偶然需要使用LRU緩存,搜索了一下,發現STL和boost裏沒有現成的。於是就用map和list簡單實現了下。通過了單元測試。因爲邏輯蠻簡單的(加上滿滿註釋不到100行),應該不會有錯哈。歡迎大家體驗。源碼除了std外,只使用了boost::function,如果不想使用boost類庫,完全可以用函數指針替換掉(註釋中有說明),但是那麼做要傳入類的成員函數就麻煩了嘿。
     LRU最簡單的實現方法,應該就是隻用一個map,然後保存每個key的對應數據和訪問時間,當容量滿的時候,找到最久未訪問的數據刪除之,插入新的。這麼做簡單是簡單,但是效率不太高。下文的實現是採用一個list保存key的訪問順序(越近訪問的在越後面),這樣最老的數據就是list.front(),無須遍歷所有記錄。比較麻煩的是,當訪問某個key的時候,需要把這個key的訪問記錄移動到list的最後,還好list提供了slice函數,可以比較高效地搞定這件事情。
     推薦一篇文章:http://timday.bitbucket.org/lru.html,下文的實現也有參考此文。

---------------------------代碼的分割線------------------------------------
#pragma once

#include <map>
#include <list>
#include <boost/function.hpp>
// 此頭文件包含XASSERT宏的定義,使用時請替換成自己的斷言函數
#include "xassert.h"

template < typename KeyType, typename ValueType >
class LRUCache
{
public:

    // 當Cache miss的時候,獲取數據的函數
    // 如果不使用boost類庫,可替換成函數指針: typedef ValueType (*FetchDataFunc)(const KeyType&)
    typedef boost::function<ValueType (const KeyType&)> FetchDataFunc;

    // 構造函數
    // func: 當Cache miss的時候,獲取數據的函數
    // capacity: Cache的容量,越大內存使用越多,也越容易命中
    LRUCache(FetchDataFunc func_, size_t capacity_)
        : fetchDataFunc(func_)
        , capacity(capacity_)
    {
        XASSERT(capacity != 0);
    }

    // 類型定義,使用者可忽略
    typedef std::list < KeyType > KeyVisitedList;
    typedef typename KeyVisitedList::iterator KeyVisitedListIterator;
    typedef std::map < KeyType, std::pair < ValueType, typename KeyVisitedList::iterator > > KeyValueMap;
    typedef typename KeyValueMap::iterator KeyValueMapIterator;

    // 獲取數據,如果Cache miss,會調用獲取數據的函數去獲取,否則直接返回Cache中的數據
    ValueType& operator[](const KeyType& k)
    {
        // 查找Cache
        KeyValueMapIterator it = keyValueMap.find(k);
        if (it == keyValueMap.end())
        {
            // Cache失效,獲取數據插入Cache
            it = insert(k, fetchDataFunc(k));           
        }
        else
        {
            // Cache命中,將該key在訪問列表中的位置移動至最後面
            keyVisitedList.splice(keyVisitedList.end(), keyVisitedList, (*it).second.second);            
        }
        return (*it).second.first;
    }

private:
    // 在Cache中插入一條記錄
    KeyValueMapIterator insert(const KeyType& k, const ValueType& v)
    {
        //    如果Cache已滿,刪除最久未被使用的元素
        if (keyValueMap.size() == capacity)
        {
            eraseOldestRecord();
        }
        // 新插入的記錄剛被訪問,所以排在訪問列表的最後
        KeyVisitedListIterator it = keyVisitedList.insert(keyVisitedList.end(), k);
      
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章