LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap)或者实现LRU(Least Recently Used最近最少算法,以access模式创建LinkedHashMap)。

下面是LinkedHashMap的get方法的代码

public V get(Object key) {
        Entry<K,V> e = (Entry<K,V>)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);
        return e.value;
    }

其中有一段:e.recordAccess(this)。下面我们进入Entry的定义

void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

这里的addBefore(lm.header)是做什么呢?再看

private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

从这里可以看到了,addBefore(lm.header)是把当前访问的元素挪到head的前面,即最近访问的元素被放到了链表头,如此要实现LRU算法只需要从链表末尾往前删除就可以了,多么巧妙的方法。

在看到LinkedHashMap之前,我以为实现LRU算法是在每个元素内部维护一个计数器,访问一次自增一次,计数器最小的会被移除。但是要想到,每次add的时候都需要做这么一次遍历循环,并取出最小的抛弃,在HashMap较大的时候效率很差。当然也有其他方法来改进,比如建立<访问次数,LinkedHashMap元素的key>这样的TreeMap,在add的时候往TreeMap里也插入一份,删除的时候取最小的即可,改进了效率但没有LinkedHashMap内部的默认实现来的简捷。

LinkedHashMap是什么时候删除的呢?

 void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex);
 
        // Remove eldest entry if instructed
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }

在增加Entry的时候,通过removeEldestEntry(eldest)判断是否需要删除最老的Entry,如果需要则remove。注意看这里Entry<K,V> eldest=header.after,记得我们前面提过LinkedHashMap还维护一个双向链表,这里的header.after就是链表尾部最后一个元素(头部元素是head.before)。

LinkedHashMap默认的removeEldestEntry方法如下

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
总是返回false,所以开发者需要实现LRU算法只需要继承LinkedHashMap并重写removeEldestEntry方法,下面以MyBatis的LRU算法的实现举例
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;
 
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };

开发者的子类并不需要直接操作eldest(上例中获得eldestKey只是MyBatis需要映射到Cache对象中的元素),只要根据自己的条件(一般是元素个数是否到达阈值)返回true/false即可。注意,要按照LRU排序必须在new LinkedHashMap()的构造函数的最后一个参数传入true(true代表LinkedHashMap内部的双向链表按访问顺序排序,false代表按插入顺序排序)。

在LinkedHashMap的注释里明确提到,该类在保持插入顺序、不想HashMap那样混乱的情况下,又没有像TreeMap那样的性能损耗。同时又能够很巧妙地实现LRU算法。其他方面和HashMap功能一致。有兴趣的同学可以仔细看看LinkedHashMap的实现。

LinkedHashMap实现LRU算法的更多相关文章

  1. LinkedHashMap 和 LRU算法实现

    个人觉得LinkedHashMap 存在的意义就是为了实现 LRU 算法. public class LinkedHashMap<K,V> extends HashMap<K,V&g ...

  2. 用LinkedHashMap实现LRU算法

    (在学习操作系统时,要做一份有关LRU和clock算法的实验报告,很多同学都应该是通过数组去实现LRU,可能是对堆栈的使用和链表的使用不是很熟悉吧,在网上查资料时看到了LinkedHashMap,于是 ...

  3. 通过LinkedHashMap实现LRU算法

    一.基于LinkedHashMap源码分析 方法调用流程(这里只是以put方法位例) put() -> putVal() -> afterNodeInsertion() -> rem ...

  4. Java集合详解5:深入理解LinkedHashMap和LRU缓存

    今天我们来深入探索一下LinkedHashMap的底层原理,并且使用linkedhashmap来实现LRU缓存. 摘要: HashMap和双向链表合二为一即是LinkedHashMap.所谓Linke ...

  5. Guava---缓存之LRU算法

    随笔 - 169  文章 - 0  评论 - 292 GuavaCache学习笔记一:自定义LRU算法的缓存实现   前言 今天在看GuavaCache缓存相关的源码,这里想到先自己手动实现一个LRU ...

  6. 借助LinkedHashMap实现基于LRU算法缓存

    一.LRU算法介绍 LRU(Least Recently Used)最近最少使用算法,是用在操作系统中的页面置换算法,因为内存空间是有限的,不可能把所有东西都放进来,所以就必须要有所取舍,我们应该把什 ...

  7. 如何用LinkedHashMap实现LRU缓存算法

    阿里巴巴笔试考到了LRU,一激动忘了怎么回事了..准备不充分啊.. 缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的.LRU这个算法就是把最近一次 ...

  8. JDK自带的LinkedHashMap来实现LRU算法

    1 代码如下 public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { private final i ...

  9. 基于LinkedhashMap实现的LRU算法

    LRU全称是Least Recently Used,即最近最久未使用的意思.LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定的空间已存 ...

随机推荐

  1. 在3D Max中查看模型引用的贴图

    需求 假如在Max中有一个模型,想查看贴图 操作步骤 1.右上角点击 2.在弹出材质编辑器中 点击吸管 3.把吸管点击在角色模型上,然后点击M 4.点击查看图像 5.就能查看到模型使用的贴图

  2. 本地不安装Oracle,plsql远程连接数据库

    由于Oracle的庞大,有时候我们需要在只安装Oracle客户端如plsql.toad等的情况下去连接远程数据库,可是没有安装Oracle就没有一切的配置文件去支持.最后终于发现一个很有效的方法,Or ...

  3. POJ 1141 Brackets Sequence

    Brackets Sequence Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29502   Accepted: 840 ...

  4. MTK 平台上查询当前使用的摄像头模组及所支持预览分辨率

    1,MTK 平台如何查询当前使用的是哪颗摄像头及相关的模组信息? 在该目录下可以查到当前平台及相关项目的配置文件 ProjectConfig.mk \ALPS.JB.MP.V1_W_20120919\ ...

  5. Flume的安装与配置

    Flume的安装与配置 一.       资源下载 资源地址:http://flume.apache.org/download.html 程序地址:http://apache.fayea.com/fl ...

  6. 结合C++和GDAL实现shapefile(shp)文件的创建和写入

    工具:vs2012+GDAL 2.0 包含头文件: #include "ogrsf_frmts.h" int main() { const char *pszDriverName ...

  7. 20135202闫佳歆--week 7 Linux内核如何装载和启动一个可执行程序--实验及总结

    week 7 实验:Linux内核如何装载和启动一个可执行程序 1.环境搭建: rm menu -rf git clone https://github.com/megnning/menu.git c ...

  8. 动画:UIKitAnimation 简单动画学习 iOS (一) 渐变 、 移动 、翻页、来回翻转 ——转载

    转载请说明(谢谢) http://blog.csdn.net/a21064346/article/details/7851695 点击打开链接 以下 一个系列的 动画效果 在 UIView.h文件中可 ...

  9. iOS——关于创建真机调试证书(发布证书,测试(调试)证书,推送调试证书)、iOS开发者账号申请 请用开发者账号去iTunes connect 查看状态

  10. PRML读书会第四章 Linear Models for Classification(贝叶斯marginalization、Fisher线性判别、感知机、概率生成和判别模型、逻辑回归)

    主讲人 planktonli planktonli(1027753147) 19:52:28 现在我们就开始讲第四章,第四章的内容是关于 线性分类模型,主要内容有四点:1) Fisher准则的分类,以 ...