LRUとは
最近のコンピュータでは、メモリは依然として非常に高価であるため、限られたメモリを適切に使用および管理してユーザーにパフォーマンスを向上させる場合、それは意味のある問題です。
LRU(最近使用されていない)は最も最近使用されていないもので、これは典型的なメモリ除去メカニズムです。
素人の言葉で言えば、LRUアルゴリズムは、最近頻繁にアクセスされるデータの保持率が高くなり、ほとんどアクセスされないデータが排除されると考えています。
LRUアルゴリズムの実装のアイデア
LRUアルゴリズムの概念によれば、次のものが必要です。
最大容量としてのパラメータキャップ
データを保存するためのデータ構造であり、必要なもの1.最新のアクセスデータを簡単に更新します。2.使用頻度の最も低いデータを簡単に見つけて、上限に達したらクリーンアップします。
ここで使用するデータ構造は、ハッシュマップ+二重リンクリストです。
1.ハッシュマップのgetメソッドとputメソッドO(1)の時間計算量を使用して、データをすばやくフェッチして保存します。
2. doublelinkedlistの機能(特定のノードの前後のノードにアクセスできます)を使用して、O(1)のデータの追加と削除を実現します。
以下に示すように:
key2が再び使用されると、対応するnode3がリンクリストの先頭に更新されます。
cap = 3とすると、key4が作成されてアクセスされると、リンクリストの最後にあるnode2が削除され、key1がクリアされます。
LRUの簡単な実装
ノードノード、ストアキー、値、プレノード、ポストノード
class Node{
public int key;
public int val;
public Node next;
public Node previous;
public Node() {
}
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
二重にリンクされたリスト。属性は、サイズ、ヘッドノード、およびテールノードです。
APIを提供します:
- addFirst():最初のメソッドをリンクリストに挿入します
- remove():最後のノードを削除します
- remove(ノードノード):特定のノードを削除します
- size():リンクリストの長さを取得します
class DoubleList{
private int size;
private Node head;
private Node tail;
public DoubleList() {
this.head = new Node();
this.tail = new Node();
size = 0;
head.next = tail;
tail.previous = head;
}
public void addFirst(Node node){
Node temp = head.next;
head.next = node;
node.previous = head;
node.next = temp;
temp.previous = node;
size++;
}
public void remove(Node node){
if(null==node|| node.previous==null|| node.next==null){
return;
}
node.previous.next = node.next;
node.next.previous = node.previous;
node.next=null;
node.previous=null;
size--;
}
public void remove(){
if(size<=0) return;
Node temp = tail.previous;
temp.previous.next = temp.next;
tail.previous = temp.previous;
temp.next = null;
temp.previous=null;
size--;
}
public int size(){
return size;
}
}
LRUアルゴリズム実装クラス
API
- get(int key):nullの場合は-1を返します
- put(int key、int value)マップに存在する場合は、元のノードを削除し、新しいノードを追加します。マップに存在しない場合は、マップとリンクリストに新しいデータを追加します。
public class LRUCache {
Map<Integer,Node> map;
DoubleList cache;
int cap;
public LRUCache(int cap) {
map = new HashMap<>();
cache = new DoubleList();
this.cap = cap;
}
public int get(int key){
Node node = map.get(key);
return node==null? -1:node.val;
}
public void put(int key, int val){
Node node = new Node(key,val);
if(map.get(key)!=null){
cache.remove(map.get(key));
cache.addFirst(node);
map.put(key,node);
return;
}
map.put(key,node);
cache.addFirst(node);
if(cache.size()>cap){
cache.remove();
}
}
public static void main(String[] args) {
//test, cap = 3
LRUCache lruCache = new LRUCache(3);
lruCache.put(1,1);
lruCache.put(2,2);
lruCache.put(3,3);
//<1,1>来到链表头部
lruCache.put(1,1);
//<4,4>来到链表头部, <2,2>被淘汰。
lruCache.put(4,4);
}
}
LRUアプリケーションシナリオ
- 低レベルのメモリ管理、ページ置換アルゴリズム
- 一般的なキャッシュサービス、memcache \ redisなど
- ビジネスシナリオの一部
参照
LRU戦略の詳細な説明と実装
LRUの原則とアプリケーションのシナリオ
著者:ZzAllenZz
ソース:https://segmentfault.com/a/1190000039256321