常见算法学习-LRU

Least Frequently Used 😆

简单原理和实现参考: 漫画:什么是LRU算法

简单版本

可以使用JDK自带的LinkedHashMap数据结构来实现

方法说明


//LinkedHashMap的一个构造函数,当参数accessOrder为true时,即会按照访问顺序排序,最近访问的放在最前,最早访问的放在后面
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
}

//LinkedHashMap自带的判断是否删除最老的元素方法,默认返回false,即不删除老数据
//我们要做的就是重写这个方法,当满足一定条件时删除老数据
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
}

实现方式

final int cacheSize = 100;
Map<String, String> map = new LinkedHashMap<String, String>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
    @Override
    protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
    return size() > cacheSize;
    }
};

手写版本

/**
 * LRU缓存实现
 *
 * @author Miguel.hou
 * @version v1.0
 * @date 2019-11-22
 */
public class LRUCache<V> {

    private Node<V> head;
    private Node<V> tail;
    private HashMap<String, Node<V>> hashMap;

    private Integer capacity;

    public LRUCache(Integer capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException("capacity must greater than 0");
        }
        this.capacity = capacity;
        hashMap = new HashMap<>();
    }

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

        // 刷新节点
        refreshNode(node);
        return node.value;
    }

    public synchronized void put(String key, V value) {
        Node<V> node = hashMap.get(key);
        if (node == null) {
            // 判断是否大于容量,需要去除首节点
            if (hashMap.size() >= capacity) {
                String rmKey = removeNode(head);
                hashMap.remove(rmKey);
            }
            // 加入队列
            Node<V> newNode = new Node<>(key, value);
            addNode(newNode);
            hashMap.put(key, newNode);
        } else {
            // node存在,刷新值
            node.value = value;
            refreshNode(node);
        }
    }

    public synchronized void remove(String key) {
        Node<V> node = hashMap.get(key);
        if (node == null) {
            return;
        }
        removeNode(node);
        hashMap.remove(key);
    }

    /**
     * 刷新节点位置
     * @param node
     */
    private void refreshNode(Node<V> node) {
        if (node == tail) {
            // 尾节点,不需要更新节点
            return;
        }

        // 删除节点
        removeNode(node);
        // 新增节点
        addNode(node);
    }

    private void addNode(Node<V> node) {
        if (tail != null) {
            tail.next = node;
            node.pre = tail;
            node.next = null;
        }
        tail = node;
        if (head == null) {
            head = node;
        }
    }

    private String removeNode(Node<V> node) {
        if (node == tail) {
            tail = node.pre;
        } else if (node == head) {
            head = node.next;
        } else {
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }

        return node.key;
    }

    class Node<V> {
        public Node(String key, V value) {
            this.key = key;
            this.value = value;
        }

        Node<V> pre;
        Node<V> next;
        String key;
        V value;
    }

    public static void main(String[] args) {
        LRUCache<String> lruCache = new LRUCache<>(3);

        lruCache.put("001", "用户1信息");
        lruCache.put("002", "用户2信息");
        lruCache.put("003", "用户3信息");
        lruCache.put("004", "用户4信息");
        lruCache.put("005", "用户5信息");

        System.out.println(lruCache.get("004"));
        System.out.println(lruCache.get("003"));
        System.out.println(123);
    }
}

2020-07-06,后面看到一版实现起来更好的,通过对头尾增加fake节点,减少了很多空值的判断,代码的逻辑也清晰了很多。

class LRUCache {
    class Node {
        int key;
        int value;

        Node pre;
        Node next;

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

    private Node head;
    private Node tail;
    private int capacity;
    private Map<Integer, Node> map;

    public LRUCache(int capacity) {
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.pre = head;
        map = new HashMap<>();
        this.capacity = capacity;
    }

    public int get(int key) {
        Node node = map.get(key);
        if (node == null) {
            return -1;
        }

        refreshNode(node);
        return node.value;
    }

    public void put(int key, int value) {
        Node node = map.get(key);
        if (node == null) {
            if (map.size() == capacity) {
                Node remove = removeNode(tail.pre);
                map.remove(remove.key);
            }

            Node newNode = new Node(key, value);
            addNode(newNode);
            map.put(key, newNode);
        } else {
            node.value = value;
            refreshNode(node);
        }
    }

    private void refreshNode(Node node) {
        removeNode(node);
        addNode(node);
    }

    private Node removeNode(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
        return node;
    }

    private void addNode(Node node) {
        Node temp = head.next;
        head.next = node;
        temp.pre = node;
        node.next = temp;
        node.pre = head;
    }
}
posted @ 2019-11-22 01:54  MiguelHou  阅读(117)  评论(0编辑  收藏  举报