在有限内存空间中,抛弃距当前时间最远被使用(访问)的元素的缓存的数据结构,能够添加元素,读取元素。这个数据结构在操作系统中经常被使用到,见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)
{
node.val=value;
if(node==this.head)
{
if(this.size()==1)
return;
head=head.next;
}
else if(node==this.tail)
return;
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;
this.tail = 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,Integer> lru=new LRUCache<>(2);
lru.get(2);
lru.put(2,6);
lru.get(1);
lru.put(1,5);
lru.put(1,2);
lru.get(1);
lru.get(2);
}
}