范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

昨天面试被问到的缓存淘汰算法FIFOLRULFU及Java实现

  缓存淘汰算法
  在高并发、高性能的质量要求不断提高时,我们首先会想到的就是利用缓存予以应对。
  第一次请求时把计算好的结果存放在缓存中,下次遇到同样的请求时,把之前保存在缓存中的数据直接拿来使用。
  但是,缓存的空间一般都是有限,不可能把所有的结果全部保存下来。那么,当缓存空间全部被占满再有新的数据需要被保存,就要决定删除原来的哪些数据。如何做这样决定需要使用缓存淘汰算法。
  常用的缓存淘汰算法有:FIFO、LRU、LFU,下面我们就逐一介绍一下。  FIFO
  FIFO,First In First Out,先进先出算法。判断被存储的时间,离目前最远的数据优先被淘汰。简单地说,先存入缓存的数据,先被淘汰。
  最早存入缓存的数据,其不再被使用的可能性比刚存入缓存的可能性大。建立一个FIFO队列,记录所有在缓存中的数据。当一条数据被存入缓存时,就把它插在队尾上。需要被淘汰的数据一直在队列头。这种算法只是在按线性顺序访问数据时才是理想的,否则效率不高。因为那些常被访问的数据,往往在缓存中也停留得最久,结果它们却因变"老"而不得不被淘汰出去。
  FIFO算法用队列实现就可以了,这里就不做代码实现了。  LRU
  LRU,Least Recently Used,最近最少使用算法。判断最近被使用的时间,目前最远的数据优先被淘汰。简单地说,LRU 的淘汰规则是基于访问时间。
  如果一个数据在最近一段时间没有被使用到,那么可以认为在将来它被使用的可能性也很小。因此,当缓存空间满时,最久没有使用的数据最先被淘汰。
  在Java中,其实LinkedHashMap已经实现了LRU缓存淘汰算法,需要在构造函数第三个参数传入true,表示按照时间顺序访问。可以直接继承LinkedHashMap来实现。  package one.more;  import java.util.LinkedHashMap; import java.util.Map;  public class LruCache extends LinkedHashMap {      /**      * 容量限制      */     private int capacity;      LruCache(int capacity) {         // 初始大小,0.75是装载因子,true是表示按照访问时间排序         super(capacity, 0.75f, true);         //缓存最大容量         this.capacity = capacity;     }      /**      * 重写removeEldestEntry方法,如果缓存满了,则把链表头部第一个节点和对应的数据删除。      */     @Override     protected boolean removeEldestEntry(Map.Entry eldest) {         return size() > capacity;     } }
  我写一个简单的程序测试一下:  package one.more;  public class TestApp {      public static void main(String[] args) {         LruCache cache = new LruCache(3);         cache.put("keyA", "valueA");         System.out.println("put keyA");         System.out.println(cache);         System.out.println("=========================");          cache.put("keyB", "valueB");         System.out.println("put keyB");         System.out.println(cache);         System.out.println("=========================");          cache.put("keyC", "valueC");         System.out.println("put keyC");         System.out.println(cache);         System.out.println("=========================");          cache.get("keyA");         System.out.println("get keyA");         System.out.println(cache);         System.out.println("=========================");          cache.put("keyD", "valueD");         System.out.println("put keyD");         System.out.println(cache);     } }
  运行结果如下:  put keyA {keyA=valueA} ========================= put keyB {keyA=valueA, keyB=valueB} ========================= put keyC {keyA=valueA, keyB=valueB, keyC=valueC} ========================= get keyA {keyB=valueB, keyC=valueC, keyA=valueA} ========================= put keyD {keyC=valueC, keyA=valueA, keyD=valueD}
  当然,这个不是面试官想要的,也不是我们想要的。我们可以使用双向链表和哈希表进行实现,哈希表用于存储对应的数据,双向链表用于数据被使用的时间先后顺序。
  在访问数据时,如果数据已存在缓存中,则把该数据的对应节点移到链表尾部。如此操作,在链表头部的节点则是最近最少使用的数据。
  当需要添加新的数据到缓存时,如果该数据已存在缓存中,则把该数据对应的节点移到链表尾部;如果不存在,则新建一个对应的节点,放到链表尾部;如果缓存满了,则把链表头部第一个节点和对应的数据删除。  package one.more;  import java.util.HashMap; import java.util.Map;  public class LruCache {      /**      * 头结点      */     private Node head;     /**      * 尾结点      */     private Node tail;     /**      * 容量限制      */     private int capacity;     /**      * key和数据的映射      */     private Map map;      LruCache(int capacity) {         this.capacity = capacity;         this.map = new HashMap<>();     }      public V put(K key, V value) {         Node node = map.get(key);         // 数据存在,将节点移动到队尾         if (node != null) {             V oldValue = node.value;             //更新数据             node.value = value;             moveToTail(node);             return oldValue;         } else {             Node newNode = new Node(key, value);             // 数据不存在,判断链表是否满             if (map.size() == capacity) {                 // 如果满,则删除队首节点,更新哈希表                 map.remove(removeHead().key);             }             // 放入队尾节点             addToTail(newNode);             map.put(key, newNode);             return null;         }     }      public V get(K key) {         Node node = map.get(key);         if (node != null) {             moveToTail(node);             return node.value;         }         return null;     }      @Override     public String toString() {         StringBuilder sb = new StringBuilder();         sb.append("LruCache{");         Node curr = this.head;         while (curr != null) {             if(curr != this.head){                 sb.append(",").append(" ");             }             sb.append(curr.key);             sb.append("=");             sb.append(curr.value);             curr = curr.next;         }         return sb.append("}").toString();     }      private void addToTail(Node newNode) {         if (newNode == null) {             return;         }         if (head == null) {             head = newNode;             tail = newNode;         } else {             //连接新节点             tail.next = newNode;             newNode.pre = tail;             //更新尾节点指针为新节点             tail = newNode;         }     }      private void moveToTail(Node node) {         if (tail == node) {             return;         }         if (head == node) {             head = node.next;             head.pre = null;         } else {             //调整双向链表指针             node.pre.next = node.next;             node.next.pre = node.pre;         }         node.pre = tail;         node.next = null;         tail.next = node;         tail = node;     }      private Node removeHead() {         if (head == null) {             return null;         }         Node res = head;         if (head == tail) {             head = null;             tail = null;         } else {             head = res.next;             head.pre = null;             res.next = null;         }         return res;     }      class Node {         K key;         V value;         Node pre;         Node next;          Node(K key, V value) {             this.key = key;             this.value = value;         }     } }
  再次运行测试程序,结果如下:  put keyA LruCache{keyA=valueA} ========================= put keyB LruCache{keyA=valueA, keyB=valueB} ========================= put keyC LruCache{keyA=valueA, keyB=valueB, keyC=valueC} ========================= get keyA LruCache{keyB=valueB, keyC=valueC, keyA=valueA} ========================= put keyD LruCache{keyC=valueC, keyA=valueA, keyD=valueD}LFU
  LFU,Least Frequently Used,最不经常使用算法,在一段时间内,数据被使用次数最少的,优先被淘汰。简单地说,LFU 的淘汰规则是基于访问次数。
  如果一个数据在最近一段时间很少被使用到,那么可以认为在将来它被使用的可能性也很小。因此,当空间满时,最小频率使用的数据最先被淘汰。
  我们可以使用双哈希表进行实现,一个哈希表用于存储对应的数据,另一个哈希表用于存储数据被使用次数和对应的数据。  package one.more;  import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors;  public class LfuCache {      /**      * 容量限制      */     private int capacity;      /**      * 当前最小使用次数      */     private int minUsedCount;      /**      * key和数据的映射      */     private Map map;     /**      * 数据频率和对应数据组成的链表      */     private Map> usedCountMap;      public LfuCache(int capacity) {         this.capacity = capacity;         this.minUsedCount = 1;         this.map = new HashMap<>();         this.usedCountMap = new HashMap<>();     }      public V get(K key) {          Node node = map.get(key);         if (node == null) {             return null;         }         // 增加数据的访问频率         addUsedCount(node);         return node.value;     }      public V put(K key, V value) {         Node node = map.get(key);         if (node != null) {             // 如果存在则增加该数据的访问频次             V oldValue = node.value;             node.value = value;             addUsedCount(node);             return oldValue;         } else {             // 数据不存在,判断链表是否满             if (map.size() == capacity) {                 // 如果满,则删除队首节点,更新哈希表                 List list = usedCountMap.get(minUsedCount);                 Node delNode = list.get(0);                 list.remove(delNode);                 map.remove(delNode.key);             }             // 新增数据并放到数据频率为1的数据链表中             Node newNode = new Node(key, value);             map.put(key, newNode);             List list = usedCountMap.get(1);             if (list == null) {                 list = new LinkedList<>();                 usedCountMap.put(1, list);             }              list.add(newNode);             minUsedCount = 1;             return null;         }     }      @Override     public String toString() {         StringBuilder sb = new StringBuilder();         sb.append("LfuCache{");         List usedCountList = this.usedCountMap.keySet().stream().collect(Collectors.toList());         usedCountList.sort(Comparator.comparingInt(i -> i));         int count = 0;         for (int usedCount : usedCountList) {             List list = this.usedCountMap.get(usedCount);             if (list == null) {                 continue;             }             for (Node node : list) {                 if (count > 0) {                     sb.append(",").append(" ");                 }                 sb.append(node.key);                 sb.append("=");                 sb.append(node.value);                 sb.append("(UsedCount:");                 sb.append(node.usedCount);                 sb.append(")");                 count++;             }         }         return sb.append("}").toString();     }      private void addUsedCount(Node node) {         List oldList = usedCountMap.get(node.usedCount);         oldList.remove(node);          // 更新最小数据频率         if (minUsedCount == node.usedCount && oldList.isEmpty()) {             minUsedCount++;         }          node.usedCount++;         List set = usedCountMap.get(node.usedCount);         if (set == null) {             set = new LinkedList<>();             usedCountMap.put(node.usedCount, set);         }         set.add(node);     }      class Node {          K key;         V value;         int usedCount = 1;          Node(K key, V value) {             this.key = key;             this.value = value;         }     } }
  再次运行测试程序,结果如下:  put keyA LfuCache{keyA=valueA(UsedCount:1)} ========================= put keyB LfuCache{keyA=valueA(UsedCount:1), keyB=valueB(UsedCount:1)} ========================= put keyC LfuCache{keyA=valueA(UsedCount:1), keyB=valueB(UsedCount:1), keyC=valueC(UsedCount:1)} ========================= get keyA LfuCache{keyB=valueB(UsedCount:1), keyC=valueC(UsedCount:1), keyA=valueA(UsedCount:2)} ========================= put keyD LfuCache{keyC=valueC(UsedCount:1), keyD=valueD(UsedCount:1), keyA=valueA(UsedCount:2)}总结
  看到这里,你已经超越了大多数人!  FIFO,First In First Out,先进先出算法。判断被存储的时间,离目前最远的数据优先被淘汰,可以使用队列实现。  LRU,Least Recently Used,最近最少使用算法。判断最近被使用的时间,目前最远的数据优先被淘汰,可以使用双向链表和哈希表实现。  LFU,Least Frequently Used,最不经常使用算法,在一段时间内,数据被使用次数最少的,优先被淘汰,可以使用双哈希表实现。
  原文链接:https://www.cnblogs.com/heihaozi/p/15944685.html?utm_source=tuicool&utm_medium=referral

2020智慧屏白皮书发布华为联合多方开启中国电视产业3。0时代7月24日,2020智慧屏白皮书发布会在京召开,由中国电子技术标准化研究院(电子四院)华为长虹美的京东方等机构和企业共同组成的智慧屏白皮书工作组,于会上发布了2020智慧屏白皮书。华为麦芒9高清图赏,高清大屏高颜值,是不是你的菜?继华为P系列nova系列新品陆续发布之后,近日,作为华为和中国电信的合作品牌,麦芒也迎来了升级,全新麦芒9正式发布。超大高清屏幕,6400万像素高清摄影,超大电池,标配大内存之外,高端制造者?小米电视大师系列为何紧随华为站队OLED阵营说到小米电视,大家首先想到的便是极致性价比,为发烧而生!不过7月2日,在小米品牌整体上探的大策略下,小米电视也开始冲击高端市场,正式推出了旗下首款高端电视小米电视大师系列65英寸O中国云计算市场头部阵营再变阵,华为云超越腾讯百度跃居第二近日,英国调研机构Canalys发布的2020年第一季度中国公共云服务市场报告显示,华为云排名位居中国云计算市场第二,市场份额(IaaSPaaS)扩大至14。1,超越了腾讯云AWS华为畅享20Pro发布,不到2000元的华为5G手机香不香?2020年6月19日,华为畅享20Pro正式发布。其配备6。5英寸90Hz高刷新率,180Hz触控采样率,对于游戏玩家来说很香分屏多任务处理,209比例,91。2屏占比则让娱乐办公深挖华为MateBookX,这样的第三代移动办公你喜欢吗?随着互联网的高速发展,以及社会节奏的日益加快,移动办公已经成为了很多人的主流工作形态,于此同时,人们对于移动办公工具的需求也越来越高。8月19日,为满足第三代移动办公需求而生的华为感受手机摄影魅力,华为P40系列超感知影像影像展开幕8月16日,华为P40系列超感知影像艺术共创影像展在北京今日美术馆盛大启幕,著名时尚策划人史焱知名摄影师黎晓亮及三纯,以及多位明星艺人等亲临现场,共同开启一场时尚与艺术盛宴。通过极华为MatePad10。8高清图赏,华为给大学生的一份特别礼物工作学习和生活均进入更高效的移动时代,全场景互联也成为时代趋势。借助一台科技装备提高自己学习工作的效率,成为很多大学生以及职场人士选择。面对大家的这一需求,华为近日推出了一款全新的细品华为FreeBudsPro无线耳机智慧动态降噪到底多强在智能手机高速发展的同时,耳机行业也迎来了一场变革,凭借着摆脱线缆束缚带来的自由体验,TWS耳机逐步取代有线耳机占据主力市场,而经过两年的发展,TWS耳机在提升佩戴舒适度音质连接和潮玩设计5G闪充手机realme真我V5正式发布,1399元起可是你的菜?2020年8月3日中国深圳全球成长最快智能手机品牌realme真我于今日召开3分钟闪充奥利给潮玩线上新品发布会,正式带来旗下V系列首款产品闪充奥利给真我V5。作为新一代5G闪充手机华为畅享20Plus图赏,天圆地方设计,更有满满亮点近几年,虽然国产手机品牌推出了不少叫好又叫卖的机型,但是说到有自己设计语言,外观极具辨识度的,或许只有华为Mate系列。尤其近两年,无论是华为Mate20的四眼车灯式摄像头设计,还
石膏板由奢入简,极简有格调在建筑材料领域,石膏浮雕有着长远的运用历史,可以追溯到1485年的英国,那个时期的建筑师把石膏浮雕运用到王室宫殿的室内空间装饰,浮雕的花纹繁复奢华,是当时欧洲贵族圈的装饰风格。因为客厅可以不要吊顶,但厨房不行极简,是时下很流行的一种生活理念,也是如今很多家庭在装修中的价值趋向。但,装修中真的可以做到一切从简吗?答案并非肯定,因为有一处地方,就没有办法简化厨房吊顶。说起厨房吊顶,它存在的做开放式厨房的人都后悔了吗?开放式厨房是一个争论不休的装修话题,就像辩论赛一样,正反双方都提出了自己的观点正方觉得在中国做开放式厨房,等于毁家反方认为使用开放式厨房几年了,没有出现任何问题,谁也说服不了谁。那ROG推出2019全系新品ROG游戏手机2售价3499起7月23日ROG玩家国度在北京召开2019夏季新品发布,在ROG发布了ROG游戏手机ROG幻15笔记本GL10电竞主机,ROGSWIFTPG65UQ巨幕电竞显示器ROGSWIFTP大众汽车大战诺基亚魔咒出品电动星球News作者蟹老板上周末,大众汽车集团搞了一场2020年媒体发布会。这两天,陆续有报道出来。大众CEO迪斯也极其罕见地在微博发了长文,解释了大众的策略。报道分成了两类。谁都想要的OTA,为什么强如大众也搞不定差不多六天前,我们的G3收到了新的OTA提醒。一次应对当下疫情的紧急更新,新增了一个叫做高温抑菌的功能。停车场停放的ID。3恰好当天晚上,我的朋友冷酷的冬瓜发表了一篇很有趣的文章狼这些红红火火的新年单品,让你的卧室好看一整年新年翻新,似乎是一个心照不宣的习惯,不仅要换新衣,还需要给家里置办一些具有年味的年货,好迎接新一年的到来。以空间卧室为例,在无法全部更新的情况下,可以添加一些有年味的单品,进行小范用几何描绘空间,会碰撞出怎样的化学反应?住宅空间在完整呈现初期,需要运用到很多元素组合,几何元素就是常见的一种。这带有数学美学的独特符号,凭借自身的线性美感及无穷尽的展示力俘获了设计师的心,成为了构筑住宅美学不可或缺的一设计师口中的适老化设计,应该是这样的熟悉梦想改造家的朋友们都知道,节目的宗旨在于聚焦一户有住房难题的家庭,并邀请设计师在有限的时间内进行改造,给予房屋全新的面貌。而这其中,令造居君影响深刻的有两期,分别是第六季第六期拒绝冰冷,她家的北欧风很有温度本案屋主独居,热爱北欧的极简,但又希望能在色彩上有所突破,避免原始的冰冷感,多一些明媚与温暖。鉴于这是套精装房,户型方正没有需要特别改动的地方,于是决定在不动硬装的情况下只做局部的墙布的空间运用,原来这么惊艳室内空间中墙面的面积是最大的,为了避免单调,通常用软装或者墙面装饰材料来装饰。墙面装饰材料的常见种类有乳胶漆墙纸墙布护墙板硅藻泥等,而墙布作为其中成员之一,它安装简单方便隔音降噪又