146.LRUキャッシングメカニズム
知っているデータ構造を使用して、1つを設計および実装しLRU (最近最少使用) 缓存机制
ます。次の操作をサポートする必要があります:get data get and write dataput。
データの取得get(key)
-キーがキャッシュに存在する場合は、キーの値(常に正の数)を取得します-1
。それ以外の場合は、を返します。
データの書き込みput(key, value)
-キーワードがすでに存在する場合は、そのデータ値を変更します。キーワードが存在しない場合は、「キーワード/値」のセットを挿入します。キャッシュ容量が上限に達すると、新しいデータを書き込む前に最も古い未使用のデータ値を削除して、新しいデータ値用のスペースを確保する必要があります。
上級:
これらの2つの操作をO(1)時間の複雑さで完了できますか?
例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得关键字 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得关键字 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
コード1
LinkedHashMapは、すべてのエンティティの二重にリンクされたリストを内部的に維持します
同時に、コンストラクターがIteratorを設定できる場合、挿入順またはアクセス順で並べ替えられます。
class LRUCache {
int capacity;
LinkedHashMap<Integer, Integer> cache;
public LRUCache(int capacity) {
this.capacity = capacity;
cache = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return cache.size() > capacity;
}
};
}
public int get(int key) {
return cache.getOrDefault(key, -1);
}
public void put(int key, int value) {
cache.put(key, value);
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
LinkedHashMapがアクセス順にソートされる方法
デフォルトでは、挿入の順序に従ってソートされます。構築方法では、アクセスの順序に従ってソートするように設定できます。
では、訪問順に並べ替えるとはどういう意味ですか?
LinkedHashMapのget(key)メソッドはそれ自体で実装され、HashMapから継承されません。get(Key)メソッドの実装が
どのように見えるかを見てみましょう。afterNodeAccess()メソッドがどのように実装されているかを見てみましょう。
//本方法的作用:将结点移动到LinkedHashMap维护的双向链表的末尾
void afterNodeAccess(Node<K,V> e) {
// move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
このメソッドは主に、二重リンクリストのポインタを移動し、着信ノードをLinkedHashMapによって維持されている二重リンクリストの最後に移動します。これにより、get(key)メソッドを介して要素にアクセスするたびに、要素は二重リンクリストの最後に移動します。 、アクセス順に並べ替えられます。つまり、keySetまたはEntrySetがIteratorを通過するたびに、アクセスされた要素が最後に表示されます(LinedHashMapのIteratorが移動すると、内部の二重リンクリストが最初から移動するためです。ポイント、エンドノードまでトラバース)
この考え方に従って、特定の条件が満たされると、二重にリンクされたリストのヘッドノードが削除され、LruCaheが実現します。
実際、LinkedHashMapはそのようなメソッドを提供してくれました。LinkedHashMapにはメソッドremoveEldestEntry(entry)があります。このメソッドをオーバーライドするだけで、必要に応じて特定の条件下でtrueを返すため、LruCache
メソッドのデフォルトの実装が実現されます。 falseを返す
保護されたブールremoveEldestEntry(Map.Entry <K、V> eldest){ falseを返す; }
LinkedHashMapのafterNodeInsertion()メソッドは、他の条件とremoveEldestEntryの戻り値に基づいて、二重にリンクされたリストのヘッダー要素を削除するかどうかを決定します。
void afterNodeInsertion(boolean evict) {
// possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
参照
LRUキャッシングメカニズム(lru-cache)は、javaバージョンの二重リンクリスト+マップを使用して実装されます