一.简介

链表在初始化时仅需要分配一个元素的存储空间,并且插入和删除新的元素也相当便捷,同时链表在内存分配上可以是不连续的内存,也不需要做任何内存复制和重新分配的操作,由此看来顺序表的缺点在链表中都变成了优势,实际上也是如此,当然链表也有缺点,主要是在访问单个元素的时间开销上。

二.LRU

缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的 CPU 缓存、数据库缓存、浏览器缓存等等。

缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略 FIFO(First In,First Out)、最少使用策略 LFU(Least Frequently Used)、最近最少使用策略 LRU(Least Recently Used)。

特点

维护一个有序单链表,越靠近链表尾部节点,越早之前访问的。当有一个新的数据访问时,我们从链表头开始顺序遍历链表。

如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来位置删除,然后再插入到链表头部。

如果此数据没有在缓存链表中,又分为两种情况:

  • 如果此时缓存未满,则将此结点直接插入到链表的头部。
  • 如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。

三.源码

单链表

public class Link {
    private long data;
    private Link next;
    public Link() {
    }
    public Link(long data) {
        this.data = data;
    }
    public long getData() {
        return data;
    }
    public void setData(long data) {
        this.data = data;
    }
    public Link getNext() {
        return next;
    }
    public void setNext(Link next) {
        this.next = next;
    }

}

处理过程

public class LruList {
    private Link first;
    private int defaultSize = 5;
    private int length = 0;

    /**
     * 查看前缀
     * @param data
     * @return
     */
    public Link findPre(long data){
        if(length <= 0){
            return null;
        }
        Link tmp = first;
        if(tmp.getData() == data){
            return tmp;
        }
        while (tmp.getNext() != null){
            Link next = tmp.getNext();
            long data1 = next.getData();
            if(data1 == data){
                return tmp;
            }
            tmp = tmp.getNext();
        }
        return null;
    }
    /**
     * 根据前缀删除数据
     * @param pre
     * @param isPre
     */
    public void removePre(Link pre,boolean isPre){
        if(isPre){
            Link tmp = pre.getNext();
            pre.setNext(tmp.getNext());
            tmp = null;
            length --;
        }else{
            pre = null;
            length --;
        }
    }

    /**
     * 插入头部数据
     * @param data
     */
    public void addHead(long data){
        Link tmp =first;
        Link link = new Link(data);
        if(tmp!= null){
            link.setNext(tmp);
        }
        first = link;
        length ++;
    }

    /**
     * 删除尾结点
     */
    public void deleteElement(){
        if(first == null){
            return;
        }
        if(first.getNext() == null){
            first = null;
            length--;
        }
        Link tmp = first;
        while (tmp.getNext().getNext() != null){
            tmp = tmp.getNext();
        }
        tmp.setNext(null);
        length-- ;
    }
    /**
     * 根据条件插入
     *
     * @param data
     */
    public void addInFind(long data){
        /**
         * 判断前缀
         * 如果有结点,找前缀删除,把数据插入到头部
         * 没有结点,判断是否空间占满(根据设置defaultSize判断)
         * ,如满,删除末尾结点,把数据插入头部,
         * 否则直接插入头部
         *
         */
        Link pre = findPre(data);
        if(pre != null){
            boolean isPre = pre.getData() != data;
            removePre(pre,isPre);
            addHead(data);
        }else if(length < defaultSize){
            addHead(data);
        }else{
            deleteElement();
            addHead(data);
        }
    }
    @Test
    public void init(){
    }
}