手写LinkedHashMap 实现LRU

1,188 阅读1分钟

LRU是什么?

LRU(Least Recently Use)是一种缓存清楚策略。最近最少使用的被清除。

如何实现?

HashMap + DobleLinkedList(双向链表),这样做的好处是可以做到O(1)的时间复杂度对缓存进行,删除和更新。

主要有两个操作:(具体的处理细节,代码有注释!)

  • 获取缓存
    • 缓存不存在 返回-1
    • 存在
  • 增加缓存存在如下情况:
    • 缓存存在
      • 相同key value相同
      • 相同key value不相同
    • 缓存不存在
      • 缓存已满
      • 缓存未满

代码采用哨兵模式,简化了繁琐的判断head和tail的操作。(空间换时间的思路)。

Talk is cheap!Just show the code!

对应leetcode LRU Cache

代码执行效率如下:

import java.util.HashMap;
import java.util.Map;

/**
 * @author: zy
 * @Date: 2020-02-07 09:12
 * @Copyright: 2019 www.lenovo.com Inc. All rights reserved.
 * leetcode LRUCache can use as testcase.
 */
public class LRUCache {
    /**
     * LinkedListHash Map
     */
    private Map<Integer, DoubleLinkedList> cache = new HashMap<>(16);

    /**
     * cache total size
     */
    private int capacity;

    /**
     * cache current size
     */
    private int size;

    /**
     * use solider design to make code sample,less if.
     */
    private DoubleLinkedList head;

    private DoubleLinkedList tail;

    public LRUCache(int capacity) {
        if (capacity == 0) {
            throw new RuntimeException("capacity can not be zero!!!");
        }
        head = new DoubleLinkedList(0, 0);
        tail = new DoubleLinkedList(0, 0);

        head.next = tail;
        tail.pre = head;

        this.capacity = capacity;
    }

    /**
     * Doubly linked list
     */
    static class DoubleLinkedList {

        private int value;

        private int key;

        DoubleLinkedList pre = null;

        DoubleLinkedList next = null;

        DoubleLinkedList(int key, int value) {
            this.value = value;
            this.key = key;
        }
    }
    /**
     * get cache
     * two situations:
     * a:
     * cache exist
     * delete old node.
     * insert node to head.
     * b.cache not exist
     * return -1
     *
     * @param key
     * @return
     */
    public int get(int key) {
        DoubleLinkedList node = cache.get(key);
        if (node == null) {
            return -1;
        }
        deleteNode(node);
        insertToHead(key, node);
        return node.value;
    }

    /**
     * put cache
     * two situations
     * a.cache exist
     * 1.same key has same value.
     * 2.same key has diff value.
     * b.cache not exist
     * 1.cache full
     * 2.cache not full
     *
     * @param key
     * @param value
     */
    public void put(int key, int value) {
        DoubleLinkedList node = cache.get(key);
        DoubleLinkedList newNode = new DoubleLinkedList(key, value);
        if (node == null) {
            insertToHead(key, newNode);
        } else {
            deleteNode(node);
            insertToHead(key, newNode);
        }
        cache.put(key, newNode);
    }

    private void insertToHead(int key, DoubleLinkedList node) {
        if (size >= capacity) {
            cache.remove(tail.pre.key);
            deleteNode(tail.pre);
        }

        node.next = head.next;

        head.next.pre = node;

        head.next = node;

        node.pre = head;

        size++;
    }

    private void deleteNode(DoubleLinkedList node) {

        node.pre.next = node.next;

        node.next.pre = node.pre;

        size--;
    }

}