单向链表设计LRU缓存

来源:互联网 发布:大数据公共服务平台 编辑:程序博客网 时间:2024/06/06 01:55

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();}

结果如下:
screenshot

几个难点

  1. 理解链表的复制

    cacheList = newNode;

    cacheList被赋予了newNode的值,cacheList就记下了newNode的当前值,当newNode再次改变时,cacheList不会改变。

  2. 单向链表的节点删除
    巧妙利用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);        }    }}