LRU Cache(Least Recent Used缓存)

时间:2019-01-11
本文章向大家介绍LRU Cache(Least Recent Used缓存),主要包括LRU Cache(Least Recent Used缓存)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在有限内存空间中,抛弃距当前时间最远被使用(访问)的元素的缓存的数据结构,能够添加元素,读取元素。这个数据结构在操作系统中经常被使用到,见wikipedia链接https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)

使用一个双端链表和一个HashMap可以在O(1)时间、O(n)空间内完成插入和查询操作。

双端链表数据结构定义如下:

private class DLinkedListNode<Key,Value>
    {
        Key key;
        Value val;
        DLinkedListNode pre;
        DLinkedListNode next;

        public DLinkedListNode(Key key,Value val)
        {
            this.key=key;
            this.val=val;
            this.pre=null;
            this.next=null;
        }
    }

LRU Cache数据结构定义如下:

public class LRUCache<Key,Value> {

    private HashMap<Key,DLinkedListNode> hashmap;
    private int capacity;      //缓存容量
    private DLinkedListNode head;   //双端链表头结点
    private DLinkedListNode tail;   //双端链表尾节点

    public LRUCache(int capacity)
    {
        this.hashmap=new HashMap<>();
        this.capacity=capacity;
        this.head=null;
        this.tail=null;
    }
}

很基础的链表操作,泛型编程的详细代码实现如下:

import java.util.HashMap;

public class LRUCache<Key,Value> {

    private HashMap<Key,DLinkedListNode> hashmap;
    private int capacity;      //缓存容量
    private DLinkedListNode head;   //双端链表头结点
    private DLinkedListNode tail;   //双端链表尾节点


    private class DLinkedListNode<Key,Value>
    {
        Key key;
        Value val;
        DLinkedListNode pre;
        DLinkedListNode next;

        public DLinkedListNode(Key key,Value val)
        {
            this.key=key;
            this.val=val;
            this.pre=null;
            this.next=null;
        }
    }

    public LRUCache(int capacity)
    {
        this.hashmap=new HashMap<>();
        this.capacity=capacity;
        this.head=null;
        this.tail=null;
    }

    public Value get(Key key)
    {
        DLinkedListNode node= hashmap.get(key);
        if(node==null)                //当hashmap中不存在这个key时,返回null
            return null;
        if(node!=this.tail)           //存在这个key时,检查是否是尾节点:如果是尾节点,则该节点位置不动;如果不是尾节点,则需要移动位置。
        {
            if(node==this.head)       //如果是头结点,则将头结点放在双端链表尾部
                head=head.next;
            else                      //如果不是头节点,则将该节点从双端链表中移到尾部
            {
                node.pre.next=node.next;
                node.next.pre=node.pre;
            }
            tail.next=node;
            node.pre=tail;
            node.next=null;
            tail=node;
        }
        return (Value)node.val;
    }

    public void put(Key key, Value value)
    {
        DLinkedListNode node=hashmap.get(key);
        if(node!=null)
        {
            if(node==this.head)
            {
                head=head.next;
            }
            else
            {
                node.pre.next=node.next;
                node.next.pre=node.pre;
            }
            tail.next=node;
            node.pre=tail;
            node.next=null;
            tail=node;
        }
        else
        {
            DLinkedListNode newNode=new DLinkedListNode(key,value);
            if(this.size()==this.capacity)
            {
                DLinkedListNode tmp=this.head;
                this.head=this.head.next;
                this.hashmap.remove(tmp.key);
            }
            if(head==null && tail==null)
            {
                this.head=newNode;
            }
            else
            {
                this.tail.next=newNode;
                newNode.pre=this.tail;
                newNode.next=null;
            }
            this.tail=newNode;
            this.hashmap.put(key,newNode);
        }
    }

    public int size()
    {
        return this.hashmap.size();
    }


    //单元测试
    public static void main(String[] args)
    {
        LRUCache<Integer,String> lru=new LRUCache<>(4);
        lru.put(1,"haha");
        lru.put(2,"xixi");
        lru.put(3,"hehe");
        lru.put(2,"zeze");
        lru.put(5,"xuxu");
        lru.get(2);
        lru.put(6,"enen");
    }
}