LRU算法与代码实现

场景

公司的业务越来越复杂,我们需要抽出一个用户系统,向各个业务系统提供用户的基本信息。

用户系统作为非常基础的应用,公司内部会有很多个系统去调用,因此一定要注意性能问题。因此在用户系统中,可以增加一个内存缓存,当然具体的信息是存放在数据库里的。每当查找一个用户时会先在哈希表中进行查询,以此来提高访问的性能。

不过把缓存数据放在内存也会导致一个问题,就是时间长了之后,内存可能会由于数据太多而溢出,那么这时候就需要有缓存清除机制来保证系统正常运行。

LRU算法

LRU全称Least Recently Used,也就是最近最少使用的意思,是一种内存管理算法,该算法最早应用于Linux操作系统。
这个算法基于一种假设:长期不被使用的数据,在未来被用到的几率也不大。因此,当数据所占内存达到一定阈值时,我们要移除掉最近最少被使用的数据。
在LRU算法中,使用了一种有趣的数据结构,这种数据结构叫作哈希链表。

哈希链表

哈希表是由若干个Key-Value组成的。在“逻辑”上,这些Key-Value是无所谓排列顺序的,谁先谁后都一样。

在哈希链表中,这些Key-Value不再是彼此无关的存在,而是被一个链条串了起来。每一个Key-Value都具有它的前驱Key-Value、后继Key-Value,就像双向链表中的节点一样。

这样一来,原本无序的哈希表就拥有了固定的排列顺序。

LRU算法演示

1. 假设使用哈希链表来缓存用户信息,目前缓存了4个用户,这4个用户是按照被访问的时间顺序依次从链表右端插入的。

2. 如果这时业务方访问用户5,由于哈希链表中没有用户5的数据,需要从数据库中读取出来,插入到缓存中。此时,链表最右端是最新被访问的用户5,最左端是最近最少被访问的用户1。

3. 接下来,如果业务方访问用户2,哈希链表中已经存在用户2的数据,这时我们把用户2从它的前驱节点和后继节点之间移除,重新插入链表的最右端。此时,链表的最右端变成了最新被访问的用户2,最左端仍然是最近最少被访问的用户1。

4. 接下来,如果业务方请求修改用户4的信息。同样的道理,我们会把用户4从原来的位置移动到链表的最右侧,并把用户信息的值更新。这时,链表的最右端是最新被访问的用户4,最左端仍然是最近最少被访问的用户1。

扫描二维码关注公众号,回复: 11144934 查看本文章
5. 后来业务方又要访问用户6,用户6在缓存里没有,需要插入哈希链表中。假设这时缓存容量已经达到上限,必须先删除最近最少被访问的数据,那么位于哈希链表最左端的用户1就会被删除,然后再把用户6插入最右端的位置。

代码实现

public class LRUCache {
    private Node head;

    private Node end;

    // 缓存存储上限
    private int limit;

    private HashMap<String, Node> hashMap;

    public LRUCache(int limit) {
        this.limit = limit;
        hashMap = new HashMap<String, Node>();
    }

    public String get(String key) {
        Node node = hashMap.get(key);
        if(node == null) {
            return null;
        }

        refreshNode(node);
        return node.value;
    }

    public void put(String key, String value) {
        Node node = hashMap.get(key);
        if(node == null) {
            // 如果key不存在,插入key-value
            if(hashMap.size() >= limit) {
                String oldKey = removeNode(head);
                hashMap.remove(oldKey);
            }

            node = new Node(key, value);
            addNode(node);
            hashMap.put(key, node);
        } else {
            // 如果key存在,刷新key-value
            node.value = value;
            refreshNode(node);
        }
    }

    public void remove(String key) {
        Node node = hashMap.get(key);
        removeNode(node);
        hashMap.remove(key);
    }

    /**
     * 
     * 刷新被访问的节点位置
     * 
     * @param node 被访问的节点
     * 
     */
    private void refreshNode(Node node) {
        // 如果访问的是尾节点,无需移动节点
        if(node == end) {
            return;
        }
        // 移除节点
        removeNode(node);
        // 重新插入节点
        addNode(node);
    }

    /**
     * 
     * 删除节点
     * 
     * @param node 要删除的节点
     * 
     */
    private String removeNode(Node node) {
        if(node == end) {
            // 移除尾节点
            end = end.pre;
        }else if(node == head) {
            // 移除头节点
            head = head.next;
        }else {
            // 移除中间节点
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }

        return node.key;
    }

    /**
     * 
     * 尾部插入节点
     * 
     * @param node 要插入的节点
     * 
     */
    private void addNode(Node node) {
        if (end != null) {
            end.next = node;
            node.pre = end;
            node.next = null;
        }

        end = node;

        if (head == null) {
            head = node;
        }
    }

    class Node {
        Node(String key, String value) {
            this.key = key;
            this.value = value;
        }
        public Node pre;
        public Node next;
        public String key;
        public String value;
    }

    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(5);
        lruCache.put("001", "用户1信息");
        lruCache.put("002", "用户1信息");
        lruCache.put("003", "用户1信息");
        lruCache.put("004", "用户1信息");
        lruCache.put("005", "用户1信息");
        lruCache.get("002");
        lruCache.put("004", "用户2信息更新");
        lruCache.put("006", "用户6信息");
        System.out.println(lruCache.get("001"));
        System.out.println(lruCache.get("006"));
    }
}

猜你喜欢

转载自www.cnblogs.com/alimayun/p/12794650.html