LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰
图示
映射关系
增删改
代码问题解释
我们基本实现原理是通过Hashmap+双链表(Entry)实现的.
为什么用hashmap+双链表,而不是单独的双链表?
原因:用hashmap找双链表比较快,容易定位到双链表的具体的单节点,进行 *** 作;而不是每次修改或者删除的时候都需要遍历整条链表为什么定义head节点和tail节点?
原因:用head节点和tail节点更容易 *** 作,尤其是在删除的时候
具体实现方法
首先需要编写一个Entry类,Entry类里面含有key,value值,还有前驱结点pre和后继节点next,重写构造函数
其次,在我们的LRUCache类定义一个head节点和tail节点,以及int一个容量池大小capacity和记录我们的链表的长度size,还有一个Hashmap
我们定义一个initlinkedList()方法,初始化我们的链表,使头尾节点相连
重写LRUCache的构造函数,赋值capacity并定义Hashmap容量池大小,size=0,调用initlinkedList()方法
定义addNode()和deleteNode()方法,每次添加节点都添加到头结点之后,删除的话让该几点的前后节点相连
定义moveToHead()方法,里面包裹着deleteNode()和addNode()方法,方便我们修改已存在的值(因为我们修改已存在的值的时候,需要删除该节点,然后在添加该节点)
定义get()方法,从hashmap表里查找该key存不存在,如果存在,就调用moveToHead()方法移至头结点,不存在就返回null
接下来定义put()方法,这个比较难,分情况
首先我们先get()一下,查看有没有该Entry节点
如果有该Entry节点,那么我们就重新赋该Entry节点的值,调用moveToHead()方法,返回,结束方法
如果没有该Entry节点,就new一个新的Entry节点,赋值key,value,调用addNode()方法,并放入Hashmap,size++
如果size==capacity,也就是链表长度已满,把最后一个节点(不是tail节点,是tail.pre)删掉(因为最后一个节点不常用,所以删掉),并把Hashmap中的该节点也删掉,size--,之后执行上面第2部 *** 作
测试得出以下语句
public static void main(String[] args) { LRUCache cache1 = new LRUCache(2); cache1.put(1, 1); cache1.put(2, 2); System.out.println("get(1)之后的头结点之前的节点: " + cache1.head.next); System.out.println("get(1)节点: " + cache1.get(1)); System.out.println("get(1)之后的头结点之后的节点: " + cache1.head.next); cache1.put(3, 3); System.out.println("put(3)之后的头结点之后的节点: " + cache1.head.next); System.out.println("get(2)节点: " + cache1.get(2)); System.out.println("get(2)之后的头结点之后的节点: " + cache1.head.next); } //最终输出 //get(1)之后的头结点之前的节点: Entry{key=2, value=2} //get(1)节点: 1 //get(1)之后的头结点之后的节点: Entry{key=1, value=1} //put(3)之后的头结点之后的节点: Entry{key=3, value=3} //get(2)节点: null //get(2)之后的头结点之后的节点: Entry{key=3, value=3}
源码
package com.heaboy.mvc; import java.util.HashMap; import java.util.Map; public class LRUCache { Entry head, tail; int capacity; int size; Mapcache; public LRUCache(int capacity) { this.capacity = capacity; initlinkedList(); size = 0; cache = new HashMap<>(capacity); } public void put(int key, int value) { Entry node = cache.get(key); if (node != null) { node.value = value; moveToHead(node); return; } if (size == capacity) { Entry lastNode = tail.pre; deleteNode(lastNode); cache.remove(lastNode.key); size--; } Entry newNode = new Entry(key, value); addNode(newNode); cache.put(key, newNode); size++; } public Integer get(int key) { Entry node = cache.get(key); if (node == null) { return null; } moveToHead(node); return node.value; } private void moveToHead(Entry node) { deleteNode(node); addNode(node); } public void addNode(Entry node) { head.next.pre = node; node.next = head.next; node.pre = head; head.next = node; } private void deleteNode(Entry node) { node.pre.next = node.next; node.next.pre = node.pre; } private void initlinkedList() { head = new Entry(); tail = new Entry(); head.next = tail; tail.pre = head; } private static class Entry { int key; int value; Entry pre; Entry next; public Entry(int key, int value) { this.key = key; this.value = value; } public Entry() { } @Override public String toString() { return "Entry{" + "key=" + key + ", value=" + value + '}'; } } public static void main(String[] args) { LRUCache cache1 = new LRUCache(2); cache1.put(1, 1); cache1.put(2, 2); System.out.println("get(1)之后的头结点之前的节点: "+cache1.head.next); System.out.println("get(1)节点: "+cache1.get(1)); System.out.println("get(1)之后的头结点之后的节点: "+cache1.head.next); cache1.put(3, 3); System.out.println("put(3)之后的头结点之后的节点: "+cache1.head.next); System.out.println("get(2)节点: "+cache1.get(2)); System.out.println("get(2)之后的头结点之后的节点: "+cache1.head.next); } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)