java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java的LRU缓存

Java中自定义LRU缓存详解

作者:晓之木初

这篇文章主要介绍了Java中自定义LRU缓存详解,基于LRU算法的缓存系统,可以在达到缓存容量上限时,清理最近最少使用的数据,为新的数据的插入腾出空间,需要的朋友可以参考下

1. LRU算法

2. 继承LinkedHashMap实现LRU缓存

通过对LinkedHashMap的学习,我们了解到:

与HashMap不同,LinkedHashMap作为链表形式的哈希表,支持元素的插入顺序或访问顺序

使用访问顺序时,通过重写removeEldestEntry()方法,可以删除最近最少使用的键值对

因此,可以通过继承LinkedHashMap、重写removeEldestEntry() 方法,实现LRU缓存

import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache extends LinkedHashMap<Integer, Integer> {
    private int capacity;
    public LRUCache(int capacity) {
        // true表示按访顺序存储键值对,最近访问的在尾部,最近最少访问在头部
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity;
    }
    public int get(int key) {
        // 根据题目要求,不存在key时,不能直接返回null值,而是需要返回默认值-1
        return super.getOrDefault(key, -1);
    }
    public void put(int key, int value) {
        super.put(key, value);
    }
}

3. 自定义LRU缓存

最原始的想法:

class DLinkedNode{
    private int key;
    private int val;
    private DLinkNode prev;
    private DLinkNode next;
}

思路进阶

最终的代码实现

通过上述分析,代码如下

import java.util.HashMap;
public class LRUCache{
    private HashMap<Integer, DLinkedNode> map;
    private DLinkedNode head;
    private DLinkedNode tail;
    private int capacity;
    private int size;
    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        // 初始化带dummy节点的双向链表
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }
    public int get(int key) {
        DLinkedNode node = map.get(key);
        if (node == null) {
            return -1;
        }
        // 被访问,需要从当前位置移动到头部
        if (head.next != node){
            removeNode(node);
            insertToHead(node);
        }
        return node.val;
    }
    public void put(int key, int value) {
        DLinkedNode node = map.get(key);
        // 如果存在,直接更新值
        if (node != null) {
            node.val = value;
            // 被访问,需要从当前位置移动到头部
            if (head.next != node) {
                removeNode(node);
                insertToHead(node);
            }
        } else {
            // 插入前,先判断是否需要腾出空间
            if (size == capacity) {
                // 从链表中删除尾结点
                DLinkedNode last = tail.prev;
                removeNode(last);
                // 从map中移除记录
                map.remove(last.key);
                size--;
            }
            // 新建节点并插入
            DLinkedNode newNode = new DLinkedNode(key, value);
            insertToHead(newNode);
            map.put(key, newNode);
            size++;
        }
    }
    // 插入节点一定是在头部
    public void insertToHead(DLinkedNode node) {
        // 分别建立与head.next和head的关联
        DLinkedNode next = head.next;
        node.next = next;
        next.prev = node;
        head.next = node;
        node.prev = head;
    }
    // 删除指定节点
    public void removeNode(DLinkedNode node) {
        DLinkedNode prev = node.prev;
        DLinkedNode next = node.next;
        prev.next = next;
        next.prev = prev;
        // 断开引用,帮助GC
        node.prev = null;
        node.next = null;
    }
}
class DLinkedNode{
     int key;
     int val;
     DLinkedNode prev;
     DLinkedNode next;
    public DLinkedNode() {
    }
    public DLinkedNode(int key, int val) {
        this.key = key;
        this.val = val;
    }
}

几点注意事项:

到此这篇关于Java中自定义LRU缓存详解的文章就介绍到这了,更多相关Java的LRU缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文