概述

LRU(Least Recently Used)最近最少使用策略就像它的名字一样,是根据数据的历史访问记录来进行淘汰数据的,其思想是“如果数据最近被访问过,那么将来被访问的几率也更高;长期不被使用的数据在将来用到的几率也不大;当数据所占内存达到一定的阈值时,将移除最近最少被使用的数据”

步骤

  1. 如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
  2. 如果此数据没有在缓存链表中,又可以分为两种情况:
    如果此时缓存未满,则将此结点直接插入到链表的头部;
    如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。

LRU缓存需要维护一个跟时间顺序关联的数据结构

使用LinkedHashMap

  1. public class LRUCache{
  2. int capacity;
  3. Map<Integer, Integer> map;
  4. public LRUCache(int capacity) {
  5. this.capacity = capacity;
  6. map = new LinkedHashMap<>();
  7. }
  8. public int get(int key) {
  9. if (!map.containsKey(key)) {
  10. return -1;
  11. }
  12. //先删除旧的位置,再放入新位置
  13. Integer value = map.remove(key);
  14. map.put(key, value);
  15. return value;
  16. }
  17. public void put(int key, int value) {
  18. if (map.containsKey(key)) {
  19. map.remove(key);
  20. map.put(key, value);
  21. return;
  22. }
  23. map.put(key, value);
  24. //超出capacity,删除最久没用的,利用迭代器,删除第一个
  25. if (map.size() > capacity) {
  26. map.remove(map.entrySet().iterator().next().getKey());
  27. }
  28. }
  29. }

使用双链表+HashMap

  1. public class LRUCache{
  2. //定义双向链表节点
  3. private class ListNode {
  4. int key;
  5. int val;
  6. ListNode pre;
  7. ListNode next;
  8. public ListNode(int key, int val) {
  9. this.key = key;
  10. this.val = val;
  11. pre = null;
  12. next = null;
  13. }
  14. }
  15. private int capacity;
  16. private Map<Integer, ListNode> map; //key->node
  17. private ListNode head; //dummy head
  18. private ListNode tail; //dummy tail
  19. public LRUCache(int capacity) {
  20. this.capacity = capacity;
  21. map = new HashMap<>();
  22. head = new ListNode(-1, -1);
  23. tail = new ListNode(-1, -1);
  24. head.next = tail;
  25. tail.pre = head;
  26. }
  27. public int get(int key) {
  28. if (!map.containsKey(key)) {
  29. return -1;
  30. }
  31. ListNode node = map.get(key);
  32. //先把这个节点删除,再接到尾部
  33. node.pre.next = node.next;
  34. node.next.pre = node.pre;
  35. moveToTail(node);
  36. return node.val;
  37. }
  38. public void put(int key, int value) {
  39. //直接调用这边的get方法,如果存在,它会在get内部被移动到尾巴,不用再移动一遍,直接修改值即可
  40. if (get(key) != -1) {
  41. map.get(key).val = value;
  42. return;
  43. }
  44. //不存在,new一个出来,如果超出容量,把头去掉
  45. ListNode node = new ListNode(key, value);
  46. map.put(key, node);
  47. moveToTail(node);
  48. if (map.size() > capacity) {
  49. map.remove(head.next.key);
  50. head.next = head.next.next;
  51. head.next.pre = head;
  52. }
  53. }
  54. private void moveToTail(ListNode node) {
  55. node.pre = tail.pre;
  56. tail.pre = node;
  57. node.pre.next = node;
  58. node.next = tail;
  59. }
  60. }

使用单链表

  1. public class LRUCache{
  2. private class ListNode {
  3. int key, val;
  4. ListNode next;
  5. public ListNode(int key, int val) {
  6. this.key = key;
  7. this.val = val;
  8. this.next = null;
  9. }
  10. }
  11. private int capacity;
  12. private Map<Integer, ListNode> map; //key-> node.pre
  13. private ListNode head; //dummy
  14. private ListNode tail;
  15. public LRUCache(int capacity) {
  16. this.capacity = capacity;
  17. map = new HashMap<>();
  18. head = new ListNode(-1, -1);
  19. tail = head;
  20. }
  21. public int get(int key) {
  22. if (!map.containsKey(key)) {
  23. return -1;
  24. }
  25. //map中存放的是要找的节点的前驱
  26. ListNode pre = map.get(key);
  27. ListNode cur = pre.next;
  28. //把当前节点删掉并移到尾部
  29. if (cur != tail) {
  30. pre.next = cur.next;
  31. map.put(cur.next.key, pre); //更新它后面node的前驱
  32. map.put(cur.key, tail);
  33. moveToTail(cur);
  34. }
  35. return cur.val;
  36. }
  37. public void put(int key, int value) {
  38. if (get(key) != -1) {
  39. map.get(key).next.val = value;
  40. return;
  41. }
  42. //不存在就new一个
  43. ListNode node = new ListNode(key, value);
  44. map.put(key, tail); //当前node的pre是tail
  45. moveToTail(node);
  46. if (map.size() > capacity) {
  47. map.remove(head.next.key);
  48. map.put(head.next.next.key, head);
  49. head.next = head.next.next;
  50. }
  51. }
  52. private void moveToTail(ListNode node) {
  53. node.next = null;
  54. tail.next = node;
  55. tail = tail.next;
  56. }
  57. }
  1. package main
  2. type Node struct {
  3. key,val int
  4. next,prev *Node
  5. }
  6. type LRUCache struct {
  7. cap int // 缓存容量
  8. cache map[int]*Node//判断数据是否存在
  9. head *Node //头指针
  10. tail *Node // 尾指针
  11. }
  12. func NewLRUCache(capacity int)*LRUCache{
  13. l :=&LRUCache{
  14. cap: capacity,
  15. cache: make(map[int]*Node),
  16. head: &Node{},
  17. tail: &Node{},
  18. }
  19. l.head.next = l.tail
  20. l.tail.prev = l.head
  21. return l
  22. }
  23. // 双链表在头部插入数据
  24. func (l *LRUCache)insertHead(node *Node){
  25. tmp := l.head.next
  26. l.head.next = node
  27. node.next = tmp
  28. tmp.prev = node
  29. node.prev = l.head
  30. }
  31. // 双链表删除数据
  32. func (l *LRUCache)remove(node *Node){
  33. n := node.next// 获取后节点
  34. p := node.prev // 获取前节点
  35. p.next = n
  36. n.prev = p
  37. node.next = nil
  38. node.prev = nil
  39. }
  40. func(l *LRUCache)Get(key int)int{
  41. if n,ok:=l.cache[key];ok{
  42. l.remove(n)
  43. l.insertHead(n)
  44. return n.val
  45. }
  46. return -1
  47. }
  48. func(l *LRUCache)Put(key int,value int){
  49. if n,ok:=l.cache[key];ok{
  50. n.val =value
  51. l.remove(n)
  52. l.insertHead(n)
  53. }else {
  54. if len(l.cache)==l.cap {//需要判断当前容器是否已经满了
  55. deleteNode := l.tail.prev
  56. delete(l.cache,deleteNode.key)
  57. l.remove(deleteNode)
  58. }
  59. n :=&Node{
  60. key: key,
  61. val: value,
  62. }
  63. l.insertHead(n)
  64. l.cache[key]= n
  65. }
  66. }
  67. func main() {
  68. }

参考

https://blog.csdn.net/fdl123456/article/details/97623208