LRU是Least Recently Used的缩写,即最近最久未使用,常用于页面置换算法,是为虚拟页式存储管理服务的。常见的实现是将最近访问过的资源【引用】放一个队列,每次将新访问的放在队首,这样最不常用的就被放到队尾,当达到一定数量后,队尾的对象就被清除。缓存的应用场景很多,尤其是高并发的环境中,可以用来控制request数量。现在以一个单项链表为例,解释下如何来实现一个LRU。
新建一个单向链表
public final class Node{
String key;
String value;
Node next;
public Node(){};
public void deleteLastNode(Node previous) {
if(this.next == null) {
previous.next = this.next;
}else{
this.next.deleteLastNode(this);
}
}
}
final修饰类,表示此类不能再被继承。
LRUCache的实现
一个LRU cacheList需要有一个长度和头节点,且需要实现get和set方法。对于set方法,每次添加一个元素的时候,除了将这个元素放在头节点,如果这个元素已经在cacheList里面,那么需要找出来并删除。
public class LRUCache{
private int size;
private Node cacheList;
public LRUCache(int size){
this.size = size;
}
public String get(String key){
Node currentNode = cacheList;
if(key.isEmpty()){
return null;
}
while(currentNode.key != null){
if(currentNode.key == key){
return currentNode.value;
}else{
currentNode = currentNode.next;
}
}
return null;
}
public void set(String key, String value){
if(!key.isEmpty()){
int count = 0;
Node newNode = new Node();
newNode.key = key;
newNode.value = value;
newNode.next = cacheList;
cacheList = newNode;
while(newNode.next != null){
if(newNode.next.key == key){
newNode.next = newNode.next.next;
break;
}else{
count ++;
newNode = newNode.next;
}
}
if(count >= size -1){
this.cacheList.
deleteLastNode(this.cacheList);
}
}
}
private void printkeys() {
int count = 0;
Node currentNode = this.cacheList;
while(currentNode != null){
System.out.println("Key-Value pair-" + count + ": " + currentNode.key + " " + currentNode.value);
count ++;
currentNode = currentNode.next;
}
}
}
现在来测试一下:
public static void main(String[] args){
LRUCache testcache = new LRUCache(5);
testcache.set("A", "A-VALUE");
testcache.set("B", "B-VALUE");
testcache.set("B", "B-VALUE");
testcache.set("C", "C-VALUE");
testcache.set("D", "D-VALUE");
testcache.set("E", "E-VALUE");
testcache.set("F", "F-VALUE");
testcache.printkeys();
}
结果如下:
几个难点
理解链表的复制
cacheList = newNode;
cacheList被赋予了newNode的值,cacheList就记下了newNode的当前值,当newNode再次改变时,cacheList不会改变。
单向链表的节点删除
巧妙利用this来遍历指定节点。注意,这个方法要定义在Node class里面而不是在LRUCache类中。public void deleteLastNode(Node previous) { if(this.next == null) { previous.next = this.next; }else{ this.next.deleteLastNode(this); } }
另外一种方法可以用来删除任意节点的key:
public void deleteNode(String key) { if(cacheList.key.equals(key)) { if(cacheList.next != null) { cacheList= cacheList.next; }else{ cacheList = null; } }else { cacheList.next.delete(this.cacheList, key); } }
在Node类里面就要定义这个方法:
public void delete(Node previous, String key){ if(this.key.equals(key)) { previous.next = this.next; }else { if(this.next != null) { this.next.delete(this, key); } } }