根据单向链表实现Lru缓存

Lru缓存

  • 单向链表
  • 实现Lru缓存

有个Lfu算法和Lru算法
Lfu算法是根据使用频率淘汰,也就是说经常判断哪个数据使用次数最少,使用次数最少的数据将会被丢弃,虚拟机就会回收这个数据占用的内存,有个一缺点,就是新添加的数据,肯定使用次数最少,容易把刚刚添加的数据给丢弃了。
Lru算法是根据使用时间淘汰,简单的说,就是按照时间访问时间来算,距离上次访问时间越久远,就越容易被丢弃,这是虚拟机会回收这个被丢弃的数据占用的内存空间。按照现实实际情况,还是有一个问题,就是短时间内新添加了大量数据,就有可能把最常用的数据给丢弃了。
两种算法各有利弊,Lfu不会丢弃最常用的数据,Lru不会丢弃最新添加的数据。
使用单项链表实现Lru算法,是感觉是最合适的,所以我先自己弄了一个单向链表。

单向链表

单向链表代码

/**
 * 自己写的单链表
 * 可以正序也可以倒序
 * 正序的好理解
 * 倒序的可以实现Lru算法
 * @param 
 */
public class MyLinkedList<T> {
    /**
     * 默认是false,类似栈结构,先存进去的,后取出来
     */
    private boolean ISASC;
    Node list;
    int size;

    /**
     * 构造函数
     * 默认是false,类似栈结构,先存进去的,后取出来
     */
    public MyLinkedList() {
        this(false);
    }

    /**
     * 指定一个排序方式
     *
     * @param ISASC true是正序,false是倒序
     */
    public MyLinkedList(boolean ISASC) {
        this.ISASC = ISASC;
    }

    /**
     * 增加
     */
    public void add(T t) {
        if (ISASC) {
            // 按照添加顺序,顺序保存的链表
            if (list == null) {
                list = new Node(t, list);
            } else {
                Node current = new Node(t, list);
                Node node = getNode(size - 1);
                node.next = current;
            }
        } else {
            //按照添加顺序,倒序保存
            list = new Node(t, list);
        }
        size++;
    }

    /**
     * 添加到指定位置
     *
     * @param index 指定位置
     * @param t     添加的对象
     */
    public void add(int index, T t) {
        if (size == 0 && index == 0) {
            add(t);
            return;
        }
        checkIndex(index);
        if (index == 0) {
            list.next = new Node(t, list);
        } else {
            Node node = getNode(index - 1);
            Node nodeNext = getNode(index);
            node.next = new Node(t, nodeNext);
        }
        size++;
    }

    /**
     * 删除第一个
     *
     * @return 返回删除的节点的数据
     */
    public T remove() {
        if (list != null) {
//            Node removeNode = list;
//            Node node = list.next;
//            list.next = null;
//            list = node;
//            return removeNode.data;
            Node node = list;
            list = list.next;
            node.next = null;
            size--;
            return node.data;
        }
        return null;
    }

    /**
     * 删除指定位置的元素
     *
     * @param index 指定位置
     * @return 返回删除的元素
     */
    public T remove(int index) {
        checkIndex(index);
        size--;
        if (index == 0) {
            return remove();
        } else {
            Node node = getNode(index - 1);
            Node current = node.next;
            Node nextNode = current.next;
            node.next = nextNode;
            current.next = null;
            return current.data;
        }

    }

    /**
     * 删除所有的节点
     */
    public void removeAll() {
        list = null;
        size = 0;
    }

    /**
     * 修改指定位置的数据
     *
     * @param index 指定位置
     * @param t     需要修改的数据
     */
    public void set(int index, T t) {
        Node node = getNode(index);
        node.data = t;
    }

    /**
     * 查询指定位置的元素
     *
     * @param index 指定位置
     * @return 返回数据
     */
    public T get(int index) {
        checkIndex(index);
        Node current = list;
        for (int i = 0; i < index; i++) {
            current = current.next;
        }
        return (T) current.data;
    }

    /**
     * 查询指定位置的节点
     *
     * @param index 指定位置
     * @return 返回节点
     */
    protected Node getNode(int index) {
        checkIndex(index);
        Node current = list;
        for (int i = 0; i < index; i++) {
            current = current.next;
        }
        return current;
    }

    /**
     * 检查索引是否越界
     *
     * @param index 角标
     */
    protected void checkIndex(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException("size=" + size + ",index=" + index);
    }


    /**
     * 节点对象
     */
    class Node {
        //数据
        T data;
        //下一个节点
        Node next;

        /**
         * 构造函数
         *
         * @param data 数据
         * @param next 下一个节点
         */
        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

    @Override
    public String toString() {
        Node node = list;
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < size; i++) {
            stringBuilder.append(node.data + " ");
            node = node.next;
        }
        return stringBuilder.toString();
    }
}

实现Lru缓存

有了上面的单向链表,再来所Lru算法,就比较简单了,最主要的就是在添加的时候判断一下容量,还有在访问数据的时候,要把访问的这个数据所在的节点放到第一个节点的位置。

/**
 * 设计一个Lru算法的容器
 * 这个是基于单向链表做的
 * @param 
 */
public class LruLinkedList<T> extends MyLinkedList<T> {
    //设置一个容量
    private int capacity;
    //默认容量给10个大小
    public LruLinkedList() {
        this(10);
    }

    /**
     * 指定缓存容量
     * @param capacity 缓存容量大小
     */
    public LruLinkedList(int capacity) {
        this.capacity = capacity;
    }

    /**
     * 添加方法
     * 因为有容量限制,所以要判断容量大小
     * @param t 添加的数据
     */
    public void put(T t){
        if (size >= capacity){
            remove(size-1);
        }
        add(t);
    }

    /**
     * 获取的方法,Lru算法,需要把最近访问的放到第一个位置
     * @param index 获取第几个
     * @return 返回获取的节点
     */
    public T getLru(int index){
        checkIndex(index);
        //拿到前一个节点
        Node nodePre = getNode(index-1);
        //这个是需要返回数据的节点
        Node currentNode = nodePre.next;
        //这个是要把前一个节点和这个节点连接的节点
        Node nextNode = currentNode.next;
        //把前一个节点和下一个节点连接上
        nodePre.next = nextNode;
        //当前节点的下一个节点设置为第一个节点
        currentNode.next = list;
        //把第一个节点设置成当前节点
        list = currentNode;
        return currentNode.data;
    }
}

想要测试我的这个Lru算法,可以直接执行下面的代码

public class TestMain {
    public static void main(String[] args) {
        testLruLinkedList();
    }

    private static void testLruLinkedList() {
        LruLinkedList<String> lruLinkedList = new LruLinkedList<>(5);
        for (int i = 0; i < 10; i++) {
            lruLinkedList.put(String.valueOf(i));
        }
        System.out.println(lruLinkedList.toString());

        lruLinkedList.put("aa");
        System.out.println(lruLinkedList.toString());

        String lru = lruLinkedList.getLru(3);
        System.out.println(lru);
        System.out.println(lruLinkedList.toString());

    }
}

你可能感兴趣的:(算法)