LRUCache缓存实现

LRU原理:

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

实现:

最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:

1. 新数据插入到链表头部;

2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;

3. 当链表满的时候,将链表尾部的数据丢弃。

下面是代码实现LRUCache缓存实现,并确保线程安全:

import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: heyongjian
 * @Date:2019/3/29
 * LRUCache核心思想:末尾淘汰制,即使用最少放置尾部,最近使用放置首尾
 * 存储结构采用ConcurrentHashMap实现线程安全读写
 * 淘汰机制采用双向链表,插入,修改效率高
 */
public class LRUCache {
    //容量大小
    private int capacity;
    //map存储
    private ConcurrentHashMap map = new ConcurrentHashMap<>();

    //头元素
    private Node head = null;
    //尾部元素
    private Node end = null;

    public LRUCache(int capacity) {
        this.capacity = capacity;
    }

    /**
     * 如果换成存在改key值,则返回对应value值,并将该节点放置首尾,否则返回-1
     * @param key key值
     * @return 返回对应的value
     */
    public int get(int key){
        //如果已经包含
        if (map.containsKey(key)){
            Node n = map.get(key);//先获取对应的值,赋给节点n
            remove(n);//删除原来位置的节点n
            setHead(n);//并重新将节点n插入到头部
            return n.value;
        }
        return -1;
    }

    /**
     *如果键值不存在,执行插入操作:如果已达到相应大小,则最近最少使用项无效;
     * @param key 键
     * @param value 值
     */
    private void set(int key,int value){
        //如果已经存在
        if (map.containsKey(key)){
            Node old = map.get(key);
            old.value = value;//当前值替换旧值
            remove(old);//同样删除
            setHead(old);//重新在头部插入
        }else {
            //不存在,则需要创建新节点
            Node created = new Node(key,value);
            if (map.size()>=capacity){//插入时,map大小已经超过当前容量
                map.remove(end.key);//则移除最少使用的元素
                remove(end);//移除最后一个节点
                setHead(created);//把当前创建的节点放值首尾
            }
            map.put(key,created);
        }
    }

    /**
     * 将当前节点移至首部
     * @param n 当前节点
     */
    private synchronized void setHead(Node n){
        //将当前的节点的下一个节点指向当前的头节点
        n.next = head;
        //将当前节点的头节点指向null
        n.pre = null;

        //如果头节点为null,则将头节点的前一个节点指向当前节点n
        if (head!=null)
            head.pre = n;
        //否则,将当前节点的赋给头节点
        head =n;
        //如果尾节点为null,则将头节点赋给尾部几点
        if (end==null)
            end=head;
    }

    /**
     * 删除节点
     * @param n 传入节点
     */
    private synchronized void remove(Node n){

        //如果当前节点的上一个节点不为null,则将当前节点的下一个节点赋给上一个节点的下一个节点,即删除当前节点
        if (n.pre!=null) n.pre.next = n.next;
        else head = n.next; //如果当前节点的上一个节点为null,则当前节点的下一个节点赋给头节点,即为头节点

        if (n.next!=null)//如果当前节点的下一个节点为null,则当前节点的上一个节点赋给当前节点的下一个节点的上一个节点
            n.next.pre=n.pre;
        //如果当前节点的下一个节点为null,则已是最后一个节点,删除当前节点无需做修改
    }
    
    //节点
    static class Node{
        int key;
        int value;
        Node pre;
        Node next;

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

    }

}

参考链接:https://www.jianshu.com/p/95b6f10ed1f3

你可能感兴趣的:(java)