设计LRU缓存结构(Java实现)

(1)是什么?
Least Recently Used (最近最少使用)
缓存淘汰算法中的一种。因为缓存是保存在内存中的,内存是很吃紧很宝贵的,所以要保证最热点的数据做缓存,冷门数据淘汰。

(2)具体在哪些场景使用?
Mysql的缓冲池,Linux OS 的虚拟文件系统VFS都有用到。

(3)具体实现步骤?
既然是缓存,那肯定就是List中存放着一大串 Key - Value 结构,我们查找目标数据的时候就是for这个List找到对应的Key,这个List可能很长,所以我们可以把热点数据排到较前面,冷门的放后面,如果超过了长度,就淘汰,移除缓存List。

传统的LRU如下:
(1)首先定义一条链表List,初始化之初,它连续加载1~6的磁盘块到缓存中
1->2->3->4->5->6 六个page cache缓存页

(2)查询3,则3的page cache命中,遍历List并且将3移至头部(加快下次检索速度)
3->1->2->4->5->6

(3)查询7,发现缓存链表中没有page cache 命中,则读磁盘,并将7加入到缓存List头部,剔除掉列尾元素6
7->3->1->2->4->5

(4)牛客也是有道LRU的算法题,描述的也是这个思路:

设计LRU缓存结构(Java实现)_第1张图片
(5)思路很清晰,也比较好做,代码实现:
首先是比较简单的调用方法:

设计LRU缓存结构(Java实现)_第2张图片

public class SplitApplication {
    private static List resultArray = new ArrayList();
    public static void main(String[] args) {
        int[][] twoArray = new int[][]{{1,1,1},{1,2,2},{1,3,3},{2,1},{1,4,4},{2,2}};
        int listSize = 3;
        testLRU(twoArray, listSize);
    }

    public static void testLRU(int[][] twoArray, int listSize){
        LRU lru = new LRU(3);
        for (int i = 0; i < twoArray.length; i++) {
            Map map = new HashMap<>(1);
            int[] oneArray = twoArray[i];
            System.out.println(Arrays.toString(oneArray));
            // 操作标志位独立拿出来
            int opt = oneArray[0];
            // 获取剩下的参数
            if (opt==1){
                // 表示set(x, y)
                lru.set(oneArray[1],oneArray[2]);
            } else if (opt == 2){
                // 表示get(x),若x未出现过或已被移除,则返回-1
                int result = lru.get(oneArray[1]);
                resultArray.add(result);
            }
        }
        System.out.println("查询结果"+resultArray.toString());
    }
}

然后重头戏当时是LRU 实体类的定义:

public class LRU {
    private List> mapList;
    int maxSize;

    public LRU(int maxSize) {
        this.maxSize = maxSize;
        this.mapList = new ArrayList<>(maxSize);
    }

    public void set(Integer key ,Integer value){
        Map map = new HashMap<>(1);
        map.put(key,value);
        if (mapList.size() >= maxSize) {
            System.out.println("超过了大小,将会扣除末尾元素再重新设置");
            this.mapList.remove(mapList.size() - 1);
        }
        this.mapList.add(map);
        System.out.println("设置新值之后:"+this.mapList.toString());
        resortList(mapList, key, value);
    }

    public Integer get(Integer key){
        Integer result = null;
        for (int i = 0; i < mapList.size(); i++) {
            Map map = this.mapList.get(i);
            result = map.get(key);
            if (result!=null){
                // 找到了才重新触发热点排序
                resortList(this.mapList, key ,result);
                return result;
            }
        }
        return -1;
    }

    public void resortList(List> list, Integer key, Integer value){
        // 把操作/读取的目标数据移动到最前面
        Map map = null;
        Iterator> iterator = list.iterator();
        while (iterator.hasNext()){
            map = iterator.next();
            if (map.get(key) == value ){
                iterator.remove();
            }
        }
        // 每次操作的目标数据都设置为热点第一位
        list.add(0, map);
        System.out.println("重新排序后:"+this.mapList.toString());
    }

    public List> getMapList() {
        return mapList;
    }

    public void setMapList(List> mapList) {
        this.mapList = mapList;
    }

    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }
}

 (1)在这里我们定义了List> mapList 结构来存放KV形式的缓存信息。通过有参构造来指定List的大小。
(2)定义Set方法往缓存List放元素,同时判断是否超出了大小,如果是,移除最后一个元素。如果不是,设置新值到头部。
(3)定义get方法获取缓存List的缓存信息,遍历List并且根据Key获取Value。
(4)不管get还是set,进行操作后都要调用 resortList 方法对List中的元素进行重新的排序。
(5)resortList 的主要任务,就是把刚才操作的数据当作热点数据移到最前面。
(6)就这么简单,考察的更多的是基础的逻辑判断和容器的使用。


执行main方法看效果:

设计LRU缓存结构(Java实现)_第3张图片

 设计LRU缓存结构(Java实现)_whiteBearClimb的博客-CSDN博客_设计lru缓存结构

你可能感兴趣的:(2021刷题-Java,java,数据结构,算法)