Source code implementation of LRU algorithm based on two-way list + HashMap

Algorithm introduction:
The Least Recently Used LRU algorithm is a cache elimination strategy. The idea of ​​this algorithm is to replace the pages that have not been used for the longest time in the most recent period. For the upgraded version of the LRUK algorithm see

Design local cache based on LRU-K algorithm to realize traffic peak shaving


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

/**
 * Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.
 * <p>
 * Implement the LRUCache class:
 * <p>
 * LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
 * int get(int key) Return the value of the key if the key exists, otherwise return -1.
 * void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.
 * The functions get and put must each run in O(1) average time complexity.
 */
public class LRUCache {

    static class Node {
        Node prev;
        Node next;

        int key;
        int value;

        public Node() {
        }

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    Map<Integer, Node> cachMap = new HashMap<>(16);
    Node head;
    Node end;

    int size;
    int capacity;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = new Node();
        end = new Node();
        head.next = end;
        end.prev = head;
    }

    public int get(int key) {
        Node node = cachMap.get(key);
        if (node == null) {
            return -1;
        }
        // 移动到链表头
        moveToHead(node, head);
        return node.value;
    }

    public void put(int key, int value) {
        Node node = cachMap.get(key);
        if (node == null) {
            Node data = new Node(key, value);
            cachMap.put(key, data);
            addToHead(data, head);
            ++size;
            if (size > capacity) {
                cachMap.remove(end.prev.key);
                delete(end.prev);
                --size;
            }
            return;
        }

        node.value = value;
        moveToHead(node, head);
    }

    /**
     * 添加到链表头
     *
     * @param node 数据节点
     * @param head 头节点
     */
    private static void addToHead(Node node, Node head) {
        node.next = head.next;
        node.prev = head;
        head.next = node;
        node.next.prev = node;
    }

    /**
     * 移除当前节点
     *
     * @param data 数据节点
     */
    private static void delete(Node data) {
        if (data.prev != null) {
            Node prev = data.prev;
            prev.next = data.next;
            data.next.prev = prev;
        }
    }

    /**
     * 把节点移动到链表头
     *
     * @param node 节点
     * @param head 头节点
     */
    private static void moveToHead(Node node, Node head) {
        // 移除node
        delete(node);

        // 把node添加到链表头
        addToHead(node, head);
    }
}

unit test

import junit.framework.TestCase;
import org.junit.Assert;

public class LRUCacheTest extends TestCase {

    public void testGet() {
        LRUCache lruCache = new LRUCache(2);
        // node 为空,结果为-1
        Assert.assertEquals(-1, lruCache.get(1));

        // node存在
        lruCache.put(1, 2);
        Assert.assertEquals(2, lruCache.get(1));

        // node存在,重复put
        lruCache.put(1, 3);
        Assert.assertEquals(3, lruCache.get(1));

        // cache数量超过容器数量,会移除链表尾节点
        lruCache.put(2, 3);
        lruCache.put(3, 4);
        Assert.assertEquals(-1, lruCache.get(1));
    }
}

Guess you like

Origin blog.csdn.net/love254443233/article/details/132366152