LRU的两种实现

869 阅读2分钟

概念

LRU(Least Recently Used)是一种淘汰策略,是为了在内存一定的条件下,基于最近最少使用的策略来淘汰元素。Redis的内存淘汰策略中就使用到了LRU。

实现

方法1:通过LinkedHashMap实现

核心点:

1.创建LinkedHashMap时,第三个参数accessOrder必须为true,为true时是基于访问排序,false时是基于插入排序,此时变成了FIFO

2.重写removeEldestEntry方法,当map大小超过容量时返回true,删除最旧的元素

public class LruLinkedHashMap<K, V> {

    private int size;

    private float factor;

    private LinkedHashMap<K, V> linkedHashMap;

    public LruLinkedHashMap(int size, float factor) {
        this.linkedHashMap = new LinkedHashMap<K, V>(size, factor, true) {

            @Override
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                boolean tooBig = size() > size;

                if (tooBig) {
                    System.out.println("最近最少使用的key=" + eldest.getKey());
                }
                return tooBig;

            }
        };
    }

    public V put(K k, V v) {
        return linkedHashMap.put(k, v);
    }

    public V get(K k) {
        return linkedHashMap.get(k);
    }

    public String toString(){
        return linkedHashMap.toString();
    }
}

方法2:HashMap+链表实现

核心点:链表的删除、添加等操作的实现细节

public class LruMySelf<K, V> {

    private int size;

    private Map<K, Node> cache;

    private Node head;

    private Node tail;

    public LruMySelf(int size) {
        this.size = size;
        this.cache = new HashMap<>();
        this.head = null;
        this.tail = null;
    }

    public void put(K key, V value) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            node.value = value;
            moveToTail(node);
        } else {
            Node node = new Node(key, value);
            cache.put(key, node);
            addToTail(node);

            if (cache.size() > size) {
                Node oldHead = removeHead();
                cache.remove(oldHead.key);
            }
        }
    }

    private Node removeHead() {
        if (null == head) {
            return null;
        }
        Node node = head;
        System.out.println("最近最少使用的key=" + node.key);
        if (head == tail) {
            head = null;
            tail = null;
        } else {
            head = head.next;
            head.pre = null;
        }

        return node;
    }

    private void addToTail(Node node) {
        if (head == null) {
            head = node;
            tail = node;
        } else {
            tail.next = node;
            node.pre = tail;
            node.next = null;
            tail = node;
        }

    }

    private void moveToTail(Node node) {
        if (node == null || node == tail) {
            return;
        }

        if (node == head) {
            head = head.next;
            head.pre = null;
        } else {
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }

        tail.next = node;
        node.pre = tail;
        node.next = null;
        tail = node;
    }

    public V get(K key) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            moveToTail(node);
            return node.value;
        } else {
            return null;
        }
    }

    public String toString() {
        if (head == null) {
            return null;
        }

        StringBuilder builder = new StringBuilder();
        builder.append("{");
        Node node = head;
        while (node != null) {
            builder.append(node.key)
                    .append("=")
                    .append(node.value)
                    .append(",");

            node = node.next;
        }

        return builder.substring(0, builder.length() - 1) + "}";
    }

    private class Node {
        private Node pre;

        private Node next;

        private K key;

        private V value;

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
            this.pre = null;
            this.next = null;
        }
    }

}