当前位置:   article > 正文

35、链表-LRU缓存

35、链表-LRU缓存

思路:

        首先要了解LRU缓存的原理,首先定下容量,每次get请求和put请求都会把当前元素放最前/后面,如果超过容量那么头部/尾部元素就被移除,所以最近最少使用的元素会被优先移除,保证热点数据持续存在。 不管放在头部还是尾部都可以。看你怎么定义

        那么如何实现呢?有两种方式第一种直接继承LinkedHashMap 这个是已经帮我们实现的,代码如下:

  1. class LRUCache extends LinkedHashMap<Integer, Integer>{
  2. private int capacity;
  3. public LRUCache(int capacity) {
  4. super(capacity, 0.75F, true);
  5. this.capacity = capacity;
  6. }
  7. public int get(int key) {
  8. return super.getOrDefault(key, -1);
  9. }
  10. // 这个可不写
  11. public void put(int key, int value) {
  12. super.put(key, value);
  13. }
  14. @Override
  15. protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
  16. return size() > capacity;
  17. }
  18. }

第二种就是手动去实现了,代码如下:

  1. class LRUCache extends AbstractLRUCache<Integer, Integer> {
  2. public LRUCache(int capacity) {
  3. super(capacity);
  4. }
  5. public int get(int key) {
  6. Integer ans = super.get(key);
  7. return ans == null ? -1 : ans;
  8. }
  9. public void put(int key, int value) {
  10. super.set(key, value);
  11. }
  12. }
  13. abstract class AbstractLRUCache<K, V> {
  14. private Map<K, Node<K, V>> keyNodeMap;
  15. private NodeDoubleLinkedList<K, V> nodeList;
  16. private final int capacity;
  17. public AbstractLRUCache(int cap) {
  18. if (cap < 1) {
  19. throw new RuntimeException("should be more than 0");
  20. }
  21. keyNodeMap = new HashMap<>();
  22. nodeList = new NodeDoubleLinkedList<>();
  23. capacity = cap;
  24. }
  25. public V get(K key) {
  26. if (keyNodeMap.containsKey(key)) {
  27. Node<K, V> res = keyNodeMap.get(key);
  28. nodeList.moveNodeToTail(res);
  29. return res.value;
  30. }
  31. return null;
  32. }
  33. public void set(K key, V value) {
  34. if (keyNodeMap.containsKey(key)) {
  35. Node<K, V> node = keyNodeMap.get(key);
  36. node.value = value;
  37. nodeList.moveNodeToTail(node);
  38. } else {
  39. Node<K, V> newNode = new Node<>(key, value);
  40. keyNodeMap.put(key, newNode);
  41. nodeList.addNode(newNode);
  42. if (keyNodeMap.size() == capacity + 1) {
  43. removeMostUnusedCache();
  44. }
  45. }
  46. }
  47. private void removeMostUnusedCache() {
  48. Node<K, V> node = nodeList.removeHead();
  49. keyNodeMap.remove(node.key);
  50. }
  51. class Node<K, V> {
  52. public K key;
  53. public V value;
  54. public Node<K, V> last;
  55. public Node<K, V> next;
  56. public Node(K key, V value) {
  57. this.key = key;
  58. this.value = value;
  59. }
  60. }
  61. class NodeDoubleLinkedList<K, V> {
  62. private Node<K, V> head;
  63. private Node<K, V> tail;
  64. public NodeDoubleLinkedList() {
  65. head = null;
  66. tail = null;
  67. }
  68. public void addNode(Node<K, V> newNode) {
  69. if (newNode == null) {
  70. return;
  71. }
  72. if (head == null) {
  73. head = newNode;
  74. tail = newNode;
  75. } else {
  76. tail.next = newNode;
  77. newNode.last = tail;
  78. tail = newNode;
  79. }
  80. }
  81. public void moveNodeToTail(Node<K, V> node) {
  82. if (this.tail == node) {
  83. return;
  84. }
  85. if (this.head == node) {
  86. this.head = node.next;
  87. this.head.last = null;
  88. } else {
  89. node.last.next = node.next;
  90. node.next.last = node.last;
  91. }
  92. node.last = this.tail;
  93. node.next = null;
  94. this.tail.next = node;
  95. this.tail = node;
  96. }
  97. public Node<K, V> removeHead() {
  98. if (this.head == null) {
  99. return null;
  100. }
  101. Node<K, V> res = this.head;
  102. if (this.head == this.tail) {
  103. this.head = null;
  104. this.tail = null;
  105. } else {
  106. this.head = res.next;
  107. res.next = null;
  108. this.head.last = null;
  109. }
  110. return res;
  111. }
  112. }
  113. }

以下是代码实现的功能要点:

  1. AbstractLRUCache 是一个抽象类,它包含了 LRU 缓存的核心逻辑,如添加节点、移动节点到链表尾部、移除最少使用的节点等。

  2. LRUCache 是 AbstractLRUCache 的具体实现,它提供了 get 和 put 方法来与缓存进行交互。这些方法调用了抽象类中的方法来实现 LRU 逻辑。

  3. Node 类代表缓存中的一个条目,包含键、值以及指向前一个和后一个节点的指针。

  4. NodeDoubleLinkedList 是一个双向链表,用于按照访问顺序维护缓存中的节点。最近访问的节点被移动到链表的尾部,而最少使用的节点位于链表的头部。

  5. 当缓存达到其容量限制时,最少使用的节点(链表头部的节点)将被移除,以确保缓存大小不超过设定的容量。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/473286
推荐阅读
相关标签
  

闽ICP备14008679号