原理:
LRU(最近最少使用)算法根据数据的访问频率,将最近访问的放在队头,将最少访问的放在队尾,当缓存在一定范围内时,就将最少访问的数据删除,对于经常访问的数据就留在内存中,提高系统性能,正常是用在操作系统的cache命中中使用。
实现:
在Android的HWUI中,通过使用LruCaches来保存使用的资源,从而提高HWUI的渲染性能,又不至于浪费太多的内存。
如图所示,用一个双向链表用于保存当前的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