1、原理

image.png
维护一个双向链表,最新访问或添加的元素插入到链表head或者tail,
假设采用尾插法,那么最新的元素都在尾部,当LRU缓存容量满的时候,就只需要把头部节点移除即可
当有查询操作,则根据key找到对应的node,并将node移到尾部,时间复杂度为O(1)

2、手写实现

  1. package algo;
  2. import java.util.Hashtable;
  3. /**
  4. * TODO 此处填写class用途
  5. *
  6. * @author weixin
  7. * @date 2020/5/9 16:25
  8. **/
  9. public class LRUCache {
  10. class DLinkedNode{
  11. int key;
  12. int value;
  13. DLinkedNode prev;
  14. DLinkedNode next;
  15. public DLinkedNode(){};
  16. public DLinkedNode(int key, int value){
  17. super();
  18. this.key = key;
  19. this.value = value;
  20. }
  21. @Override
  22. public String toString() {
  23. StringBuilder stringBuilder = new StringBuilder();
  24. stringBuilder
  25. .append("DLinkedNode{ key=")
  26. .append(key)
  27. .append(", value=")
  28. .append(value)
  29. .append(", prev=")
  30. .append(prev == null ? null : prev.key)
  31. .append(", next=")
  32. .append(next == null ? null : next.key);
  33. return stringBuilder.toString();
  34. }
  35. }
  36. /**
  37. * 缓存当前大小
  38. */
  39. private int size;
  40. /**
  41. * 缓存容量
  42. */
  43. private int capacity;
  44. private DLinkedNode head;
  45. private DLinkedNode tail;
  46. private Hashtable<Integer,DLinkedNode> cache;
  47. public LRUCache(int capacity) {
  48. this.size = 0;
  49. this.capacity = capacity;
  50. head = new DLinkedNode();
  51. tail = new DLinkedNode();
  52. //构建一个双向链表
  53. head.next = tail;
  54. tail.prev = head;
  55. cache = new Hashtable<Integer, DLinkedNode>();
  56. }
  57. public int get(int key) {
  58. DLinkedNode node = cache.get(key);
  59. if(null == node){
  60. return -1;
  61. }
  62. //将node移到尾部
  63. move2Tail(node);
  64. return node.value;
  65. }
  66. private void move2Tail(DLinkedNode node){
  67. node.next = tail;
  68. node.prev = tail.prev;
  69. tail.prev.next = node;
  70. tail.prev = node;
  71. }
  72. private void popNode(DLinkedNode node){
  73. System.out.println("removeNode: "+node);
  74. DLinkedNode second = node.next;
  75. head.next = second;
  76. second.prev = head;
  77. node.next = null;
  78. node.prev = null;
  79. }
  80. public void put(int key, int value) {
  81. DLinkedNode node = cache.get(key);
  82. //存在则覆盖
  83. if(node != null){
  84. node.value = value;
  85. cache.put(key,node);
  86. return;
  87. }
  88. size++;
  89. if(size>capacity){
  90. popNode(head.next);
  91. }
  92. //新增node
  93. DLinkedNode nNode = new DLinkedNode(key,value);
  94. addNode(nNode);
  95. cache.put(key,nNode);
  96. //超过容量,删除头部节点
  97. }
  98. private void addNode(DLinkedNode node){
  99. //添加到尾部
  100. tail.prev.next = node;
  101. node.prev = tail.prev;
  102. node.next= tail;
  103. tail.prev = node;
  104. System.out.println("add node: "+ node);
  105. }
  106. private void removeNode(DLinkedNode node){
  107. head.next = node.next;
  108. node.next.prev = head;
  109. node.next = null;
  110. node.prev = null;
  111. }
  112. public static void main(String[] args) {
  113. LRUCache lruCache = new LRUCache(4);
  114. for (int i = 1; i<10; i++) {
  115. lruCache.put(i,i);
  116. System.out.println(lruCache.get(i));
  117. }
  118. }
  119. }