LRU(最近最少使用)算法根据数据的访问频率,将最近访问的放在队头,将最少访问的放在队尾,当缓存在一定范围内时,就将最少访问的数据删除,对于经常访问的数据就留在内存中,提高系统性能,正常是用在操作系统的cache命中中使用。
如图所示,用一个双向链表用于保存当前的Caches节点,队头的为最年轻的节点,队尾的为最老的节点,同时用一个map来保存当前的节点信息,便于提高查找的性能。
1. 当取得一个节点时(get),如果该节点在map中,则将取得当前节点在链表中的位置,将当前节点从链表中移除,同时将该节点插入到链表头的位置,即作为最年轻的节点。
2. 当需要插入一个节点时,直接将该节点插入到链表表头的位置。(Youngest)
3. 当需要删除最尾部的节点时,直接将当前的Oldest删除,将Oldest的父亲作为最老的节点。
如下代码所示,通过使用模板类实现LruCache算法,便于以后的使用:
#pragma once #include <map> using std::map; template<class TKey, class TValue> class OnEntryCallBack { public: virtual ~OnEntryCallBack(){} virtual void deleteOp(TKey& key, TValue& value) {}; virtual void dump(const TKey& key, const TValue& value){} }; template<class TKey, class TValue> class LruCaches { struct Entry; public: LruCaches(); ~LruCaches(); void setEntryCallBackListen(OnEntryCallBack<TKey, TValue>* listen) { mListen = listen; } int size() { return mTable.size(); } bool put(const TKey& key, const TValue& value); const TValue& get(const TKey& key); bool remove(const TKey& key); bool removeOldest(); void clear(); void dump(); private: void attachToCache(Entry* entry); void detachFromCache(Entry* entry); bool removeEntrye(Entry* entry); private: struct Entry { TKey mKey; TValue mValue; Entry* mParent; Entry* mChild; Entry(const TKey& key, const TValue& value) { mKey = key; mValue = value; mParent = mChild = NULL; } }; private: map<TKey, Entry*> mTable; Entry* mYoungest; Entry* mOldest; OnEntryCallBack<TKey, TValue>* mListen; }; template<class TKey, class TValue> LruCaches<TKey, TValue>::LruCaches() { mListen = NULL; mYoungest = mOldest = NULL; } template<class TKey, class TValue> LruCaches<TKey, TValue>::~LruCaches() { clear(); } template<class TKey, class TValue> bool LruCaches<TKey, TValue>::put(const TKey& key, const TValue& value) { Entry* entry = NULL; map<TKey, Entry*>::iterator iter = mTable.find(key); if (iter == mTable.end()) { entry = new Entry(key, value); mTable[key] = entry; attachToCache(entry); } else { return false; } return true; } template<class TKey, class TValue> void LruCaches<TKey, TValue>::attachToCache(Entry* entry) { if (NULL == mYoungest && NULL == mOldest) { mYoungest = mOldest = entry; } else { mYoungest->mParent = entry; entry->mChild = mYoungest; mYoungest = entry; } } template<class TKey, class TValue> void LruCaches<TKey, TValue>::detachFromCache(Entry* entry) { if (NULL != entry->mParent) { entry->mParent->mChild = entry->mChild; } else { mYoungest = entry->mChild; } if (NULL != entry->mChild) { entry->mChild->mParent = entry->mParent; } else { mOldest = entry->mParent; } entry->mParent = entry->mChild = NULL; } template<class TKey, class TValue> const TValue& LruCaches<TKey, TValue>::get(const TKey& key) { Entry* entry = NULL; map<TKey, Entry*>::iterator iter = mTable.find(key); if (iter == mTable.end()) { return NULL; } else { entry = iter->second; detachFromCache(entry); } attachToCache(entry); return entry->mValue; } template<class TKey, class TValue> bool LruCaches<TKey, TValue>::remove(const TKey& key) { map<TKey, Entry*>::iterator iter = mTable.find(key); if (iter == mTable.end()) { return false; } return removeEntrye(iter->second); } template<class TKey, class TValue> bool LruCaches<TKey, TValue>::removeOldest() { if (NULL == mOldest) { return false; } return removeEntrye(mOldest); } template<class TKey, class TValue> bool LruCaches<TKey, TValue>::removeEntrye(Entry* entry) { detachFromCache(entry); map<TKey, Entry*>::iterator iter = mTable.find(entry->mKey); if (iter == mTable.end()) { return false; } mTable.erase(iter); mListen->deleteOp(entry->mKey, entry->mValue); delete entry; entry = NULL; return true; } template<class TKey, class TValue> void LruCaches<TKey, TValue>::clear() { map<TKey, Entry*>::iterator iter = mTable.begin(); for (iter; iter != mTable.end(); iter++) { mListen->deleteOp(iter->second->mKey, iter->second->mValue); delete iter->second; } mTable.clear(); } template<class TKey, class TValue> void LruCaches<TKey, TValue>::dump() { Entry* tmp = mYoungest; for (tmp; tmp != NULL; tmp = tmp->mChild) { mListen->dump(tmp->mKey, tmp->mValue); } }
class LruTest : public OnEntryCallBack<int, int> { public: LruTest() { Caches.setEntryCallBackListen(this); } ~LruTest(){} void init() { Caches.put(1, 10); Caches.put(2, 20); Caches.put(3, 30); Caches.put(4, 40); Caches.get(2); cout<<endl; Caches.get(1); cout<<endl; Caches.get(3); cout<<endl; Caches.get(2); cout<<endl; Caches.removeOldest(); Caches.dump(); cout<<endl; Caches.removeOldest(); Caches.dump(); cout<<endl; Caches.removeOldest(); Caches.dump(); cout<<endl; Caches.removeOldest(); Caches.dump(); Caches.size(); Caches.clear(); Caches.dump(); } virtual void dump(const int & key, const int & value) { cout<<"key="<<key <<" value="<<value<<endl; } private: LruCaches<int, int> Caches; };
具体也可以参看Android源码:
system/core/include/utils/LruCache.h