LRU队列即最少使用算法,核心思想是:数据最近被访问过,那么它被访问的几率更高。扩展起来解释即如果某组数据被频繁访问,那么这组数据应该放在最显眼的地方(以最快速度取到),至于访问几率比较低的数据可以放在某个角落。
以程序角度实现方式:以list作为数据缓存队列,访问几率高的数据放在list的头,访问几率低的数据放在list尾部,如此,访问几率越高的数据存取速度越快。
对于LRU实现方式,可以采用链表。但在现代编译器的优化下,采用std::list未必不是一种好的方式,能够让代码更精简(越复杂的代码出错的可能性越高)。明确算法核心内容之后,确定对外接口:
- Put,放入数据,内部需要进行访问几率判断。
- Get,获取数据。
- Del,删除。
代码实现在这里:https://github.com/shaoyuan1943/LRU
#pragma once
#ifndef _LRU_H_
#define _LRU_H_
#include <string>
#include <functional>
#include <map>
#include <list>
namespace lru
{
template<typename T>
class Entry final
{
public:
std::wstring key;
T value;
};
template<typename T>
using Iter = typename std::list<Entry<T>>::iterator;
template<typename T>
using EvictCallback = typename std::function<void(const Entry<T>& t)>;
template<typename T>
class List final
{
public:
List()
{
}
~List()
{
lst_.clear();
}
unsigned int Capacity() const
{
return capacity_;
}
Iter<T> PushFront(const Entry<T>& entry)
{
++capacity_;
return lst_.insert(lst_.begin(), entry);
}
Iter<T> MoveToFront(const Iter<T>& it)
{
auto v = (*it);
lst_.erase(it);
return lst_.insert(lst_.begin(), v);
}
bool Remove(const Iter<T>& it)
{
if (lst_.size() > 0 && capacity_ > 0)
{
--capacity_;
lst_.erase(it);
return true;
}
return false;
}
Iter<T> Back()
{
if (lst_.size() > 0 && capacity_ > 0)
{
return (--lst_.end());
}
return lst_.end();
}
bool IsEnd(Iter<T> it) const
{
return it != lst_.end();
}
private:
unsigned int capacity_{ 0 };
std::list<Entry<T>> lst_;
};
template<typename T>
class LRU final
{
public:
LRU(const unsigned int& fixed, const EvictCallback<T>& callback = nullptr)
{
if (fixedCapacity_ != fixed)
fixedCapacity_ = fixed;
if (callback != nullptr)
evictCallback_ = callback;
}
~LRU()
{}
bool Put(const std::wstring& key, const T& value)
{
if (key.empty())
return false;
auto it = lruMap_.find(key);
if (it != lruMap_.end())
{
auto newIt = lruList_.MoveToFront(it->second);
lruMap_[key] = newIt;
return false;
}
if (fixedCapacity_ <= lruList_.Capacity())
{
auto delIt = lruList_.Back();
if (lruList_.IsEnd(delIt))
{
_Delete(delIt);
}
else
return false;
}
Entry<T> entry;
entry.key = key;
entry.value = value;
auto iter = lruList_.PushFront(entry);
lruMap_[key] = iter;
return true;
}
T* Get(const std::wstring& key)
{
if (key.empty())
return nullptr;
auto it = lruMap_.find(key);
if (it == lruMap_.end())
return nullptr;
auto newIt = lruList_.MoveToFront(it->second);
lruMap_[key] = newIt;
return &newIt->value;
}
bool Del(const std::wstring& key)
{
if (key.empty())
return false;
if (lruList_.Capacity() <= 0)
return false;
auto it = lruMap_.find(key);
auto entry = (*it);
if (it == lruMap_.end())
return false;
_Delete(it->second);
return true;
}
private:
void _Delete(const Iter<T>& it)
{
if (!lruList_.IsEnd(it))
return;
auto entry = *(it);
if (lruList_.Remove(it))
{
if (evictCallback_ != nullptr)
evictCallback_(entry);
lruMap_.erase(entry.key);
}
}
private:
unsigned int fixedCapacity_{ 100 };
std::map<const std::wstring, Iter<T>> lruMap_;
List<T> lruList_;
EvictCallback<T> evictCallback_{ nullptr };
};
}
#endif