LRU缓存
实现摘要:map+双向链表
Java中的LinkedHashMap本身自带lru属性。
java实现:v1版本
feature
- 实现基本的lru语义
- 不能扩容
- evict回调
- 范型
public interface LruCache<K, V> {
V get(K key);
V put(K key, V value);
V remove(K key);
int size();
}
public class LruCacheImpl<K, V> implements LruCache<K, V> {
static class Node<K, V> {
Node<K, V> prev;
Node<K, V> next;
Node<K, V> nextE;
K key;
V value;
public Node() {
}
public Node (Node<K, V> prev, Node<K, V> next, Node<K, V> nextE, K key, V value) {
this.prev = prev;
this.next = next;
this.nextE = nextE;
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "[key: " + key + ", value:" + value + "]";
}
}
private Node<K, V> head = new Node<>(null, null, null, null, null), rear = head;
private int threshold = 0, size = 0;
public LruCacheImpl(int threshold) {
this.threshold = threshold;
}
private int capacity = 128;
private Node<K, V>[] bucket = new Node[capacity];
@Override
public V get(K key) {
//计算hash
int index = indexFor(key);
Node<K, V> e;
if ((e = bucket[index]) != null) {
for (;e != null;) {
if (e.key == key || e.key.equals(key)) {
//adjust bi linked list
linkToLast(e);
return e.value;
}
e = e.nextE;
}
}
return null;
}
@Override
public V put(K key, V value) {
//计算hash
int index = indexFor(key);
Node<K, V> e;
if ((e = bucket[index]) != null) {
for (;e != null;) {
if (e.key == key || e.key.equals(key)) {
V old = e.value;
e.value = value;
//adjust bi linked list
linkToLast(e);
return old;
}
e = e.nextE;
}
}
//insert
Node<K, V> node = new Node<>(rear, null,null, key, value);
rear.next = node;
rear = node;
if (head.next == null) {
head.next = node;
}
bucket[index] = node;
size++;
if (size > threshold) {
int toDelNum = size - threshold;
Node<K, V> p = head.next;
for (int i = 0; i < toDelNum && p != null; i++, p = p.next) {
remove(p.key);
onEvict(p);
}
}
return value;
}
//callback
private void onEvict(Node<K,V> p) {
System.out.println("evict : " + p);
}
private void linkToLast(Node<K,V> e) {
if (e == rear) return;
//先删除该节点
e.next.prev = e.prev;
e.prev.next = e.next;
rear.next = e;
e.next = null;
e.prev = rear;
rear = e;
}
private int indexFor(K key) {
int hash = key.hashCode();
return hash % capacity;
}
@Override
public V remove(K key) {
//计算hash
int index = indexFor(key);
Node<K, V> e;
if ((e = bucket[index]) != null) {
if (e.key == key || e.key.equals(key)) {
bucket[index] = e.nextE;
} else {
for (Node<K, V> pre = e, cur = e.nextE; cur != null; pre = cur, cur = cur.nextE) {
if (cur.key == key || cur.key.equals(key)) {
//delete node @ bi linked list
pre.nextE = cur.nextE;
cur.nextE = null;
e = cur;
break;
}
}
}
//delete e @ bi linked list
size--;
if (e == rear) {
rear = e.prev;
rear.next = null;
} else {
e.prev.next = e.next;
e.next.prev = e.prev;
}
e.next = null;
e.prev = null;
return e.value;
}
return null;
}
@Override
public int size() {
return size;
}
public void list() {
StringBuilder sb = new StringBuilder();
for (Node<K, V> e = head.next; e != null; e = e.next) {
sb.append("\n[key=").append(e.key).append(", value=").append(e.value).append("]");
}
System.out.println(sb.toString());
}
}