算法:LRU 缓存实现

本贴最后更新于 1281 天前,其中的信息可能已经时移世易

概述

LRU 缓存算法,是提供一种缓存实现,要求插入新数据时,当缓存达到最大空间要求,就将最久没有访问过的数据删除掉从而为新数据腾出空间。同时要求获取或插入数据的时间复杂度都为 O(1)。

基本思路

  • hash 表 + 链表

hash 表的目的是为了使访问的事件复杂度为 O(1),链表是为了维护一种时间维度上是否最近被访问的前后关系。其中 hash 表使用 Java 中的 HashMap 来替代,链表部分自己实现。

代码

代码中附带很多注释,具体逻辑请参考注释

package top.hudk.cache; import java.util.HashMap; import java.util.Map; /** * 作用: LRU(最近最少使用)缓存实现, * 提供加入缓存 put(k,v) 和从缓存获取数据get(k)两种方法, * 当加入缓存的数据已经将要超过缓存容量时, * 就将缓存中最久没有被访问的数据删除, * 从而为新数据腾出空间。 * 如果加入新数据时,缓存中已经存在该键, * 那么将该键对应的数据标记为最新访问,并更新其内容。 * * @author hudk * @date 2020/5/26 17:02 */ public class LRUCache<K,V> { /** * 初始容量 */ private int capacity; public LRUCache(int capacity) { this.capacity = capacity; } /** * 哈希表,用于在O(1)的时间内找到目标元素 */ private Map<K, Node> map = new HashMap<>(); /** * 链表尾部元素,每次访问,都会将访问的数据移动到尾部 */ private Node<K, V> tail; /** * 链表头部元素,每次空间不足,都会删除头元素对应的数据 */ private Node<K, V> head; /** * 添加新的键值对元素 * * @param key * @param value * @return */ public V put(K key, V value) { //若新增的内容,缓存中没有,且当下容量已经达到最大容量,则将头部元素删除(头部元素是最久没有访问过的元素) if (!map.containsKey(key) && map.size() == capacity) { deleteHead(); } //如果缓存中已经存在该键的内容,则将其在链表中的位置移到链表的尾部,并更新其内容。 if (map.containsKey(key)) { setLastAndUpdate(key, value); } else { //如果缓存没有该键的内容,则直接将其追加到尾部 linkLast(key, value); } return value; } /** * 从缓存中获取指定键对应的值内容 * * @param key * @return */ public V get(K key) { //如果该键存在缓存中,则将该键值元素在链表中的位置,移到链表尾部,并返回其值内容 if (map.containsKey(key)) { setLast(key); return this.tail.item; } else { //如果缓存中没有找到该键对应的数据,则返回null return null; } } /** * 将新的元素,放到链表尾部,并加入到哈希表中 * * @param key * @param value */ private void linkLast(K key, V value) { Node<K, V> tail = this.tail; Node<K, V> newNode = new Node<>(key, value, null, this.tail); this.tail = newNode; if (tail == null) { this.head = newNode; } else { tail.next = newNode; } map.put(key, newNode); } /** * 删除链表头部元素 */ private void deleteHead() { if (this.head != null) { map.remove(this.head.kay); this.head.kay = null; this.head.item = null; Node next = this.head.next; if (next != null) { this.head.next.prev = null; this.head.next = null; this.head = next; } } } /** * 将集合中已有的指定元素,放到尾部,并更新它的内容 * * @param key * @param value */ private V setLastAndUpdate(K key, V value) { setLast(key); return this.tail.item = value; } /** * 将集合中已有的指定元素,放到尾部 * * @param key */ private V setLast(K key) { Node<K, V> node = map.get(key); if (this.head == this.tail || node == this.tail) { return this.tail.item; } if (node.next != null && node.prev != null) { node.prev.next = node.next; node.next.prev = node.prev; } if (node == this.head) { node.next.prev = null; this.head = node.next; } this.tail.next = node; node.prev = tail; node.next = null; this.tail = node; return this.tail.item; } /** * 双向链表元素 * * @param <K> * @param <V> */ private static class Node<K, V> { K kay; V item; LRUCache.Node<K, V> next; LRUCache.Node<K, V> prev; Node(K kay, V element, LRUCache.Node<K, V> next, LRUCache.Node<K, V> prev) { this.kay = kay; this.item = element; this.next = next; this.prev = prev; } } }
  • 算法
    436 引用 • 254 回帖 • 24 关注
1 操作
hudk 在 2021-10-11 10:53:28 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...