zoukankan      html  css  js  c++  java
  • LRU缓存的实现


    LRU简介

    LRU是“Least Recently Used”的简写,意思是最近最少使用,是一种缓存淘汰策略,在有限的缓存资源中,淘汰掉最近最久未使用的。例如:缓存最大容纳10000条数据,在添加时,只要数据总数小于等于10000可以随意添加,但是当数据量大于1万时,将旧的数据删除,再添加新的数据;添加时,要将新来的数据添加到最前面。说完来添加,再来说查询,从缓存中获取的数据返回之前,需要将数据移动到最前面,因为获取改数据就代表最近使用了,对于缓存来说,最近这个数据很有可能会被再次使用,所以要移动到最前面。

    LRU


    LRU算法分析

    因为缓存具有查询快(时间复杂度必须是O(1)),增加快,删除快的特点。同时,LRU策略分析的数据有新旧之分,新的在最前面,旧的往后放。因此选取数据结构时,要遵循有序的特点。因此可以将HashMap与双向链表结合起来实现LRUCache。Hash表查询效率是O(1),但是不是有序的,双向链表(此处不是循环链表)有序并且增加与删除都是O(1),但查询是O(n),所以两者互相取长补短。

    在这里插入图片描述

    实现代码

    节点类

    public class Node {
        public int key,value;
        public Node prev;
        public Node next;
    
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    
        @Override
        public String toString() {
            return "Node{" +
                    "key=" + key +
                    ", value=" + value +
                    '}';
        }
    }
    

    双向链表

    public class DoubleList {
        private LinkedList<Node> linkedList = new LinkedList();
    
        /**添加第一个节点
         * @param node
         */
        public void addFirst(Node node){
            linkedList.addFirst(node);
        }
    
    
        /**
         *移除最后一个节点
         */
        public Node removeLast(){
            return linkedList.removeLast();
        }
    
    
        /**
         *移除节点
         */
        public void remove(Node node){
            linkedList.remove(node);
        }
    
    
        /**
         *  链表长度
         * @return
         */
        public int size(){
            return linkedList.size();
        }
    
        /**
         * 输出双向链表
         */
        public void printAll(){
            Iterator<Node> iterator = linkedList.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
        }
    }
    

    LRUCache类

    public class LRUCache {
        private HashMap<Integer, Node> map;
        private DoubleList cache;
        private int cap;
    
        public LRUCache( int cap) {
            this.map = new HashMap<Integer, Node>();
            this.cache = new DoubleList();
            this.cap = cap;
        }
    	
    	/**
    	 * 查询value
    	 **/
        public int get(int key){
            // 首先判断key是否在map中
            if (map.containsKey(key)){
                int x = map.get(key).value;
                // 在返回之前,将最近访问的键值对添加到LRU中
                put(key,x);
                return x;
            }else {
                // 不存在返回-1
                return -1;
            }
        }
    	
    	// 新增键值对
        public void put(int key,int value){
            Node node = new Node(key, value);
            // 首先判断key是否在map中,如果已存在,map直接新增,覆盖旧的value
            if (map.containsKey(key)){
                // 链表中删除旧的节点,在头部添加新的节点
                cache.remove(map.get(key));
                cache.addFirst(node);
                map.put(key,node);
    
            }else{
                // 在向LRU添加key时,如果缓存已满,要删除最后一个Node,同时map中也要记得删除该key
                if (cache.size()==cap){
                    Node last = cache.removeLast();
                    map.remove(last.key);
                }
                cache.addFirst(node);
                map.put(key,node);
            }
    
        }
    	
    	// 输出缓存中的数据
       public void listAll(){
            cache.printAll();
       }
    
    }
    
    

    测试类

    class LRUCacheTest {
        private LRUCache lruCache = new LRUCache(4);
    
        @Test
        public void LRUCacheTest() {
            lruCache.put(1, 2);
            lruCache.put(2, 3);
            lruCache.put(3, 4);
            lruCache.put(5, 6);
            // 因为cache大小是4,再添加新的数据时,会将最早加入缓存的数据淘汰掉
            lruCache.put(6, 67);
            // 获取第二次添加的数据,获取后(2,3)会被移动到最前边
            lruCache.get(2);
            lruCache.listAll();
        }
    
    }
    

    所以输出结果:
    在这里插入图片描述

    总结

    对于LRU算法,他的优势在于对热点数据的查询,使用LRU可以有效提高查询效率,但是对于批量查询历史数据,有可能导致缓存污染,偶然的查询历史数据,会覆盖掉热点数据,导致本次查询不再走缓存。

  • 相关阅读:
    离散数学概论
    Linux内核分析
    程序的本质
    常见bug分析
    java编程思想--学习心得
    领域特定语言--心得
    Linux下网卡配置
    ubuntu下安装python的gevent模块遇到的一个问题
    二分图的最小点覆盖和最大独立集
    win7通过ssh远程登录mininet虚拟机,运行wireshark并通过x11在宿主机显示图形界面
  • 原文地址:https://www.cnblogs.com/itjiangpo/p/14181337.html
Copyright © 2011-2022 走看看