热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LRUMap源代码实现解读

本文通过对Apache Commons Collections 项目中LRUMap这个集合类的源代码进行详细解读,为帮助大家更好的了解这个集合类的实现原理以及使用如何该集合类。 首先介

本文通过对Apache Commons Collections 项目中LRUMap这个集合类的源代码进行详细解读,为帮助大家更好的了解这个集合类的实现原理以及使用如何该集合类。

首先介绍一下LRU算法. LRU是由Least Recently Used的首字母组成,表示最近最少使用的含义,一般使用在对象淘汰算法上。也是比较常见的一种淘汰算法。

 

LRUMap 则是实现的LRP算法的Map集合类,它继承于AbstractLinkedMap 抽象类。LRUMap的使用说明如下:

LRUMap的初始化时需要指定最大集合元素个数,新增的元素个数大于允许的最大集合个数时,则会执行LRU淘汰算法。所有的元素在LRUMap中会根据最近使用情况进行排序。最近使用的会放在元素的最前面(LRUMap是通过链表来存储元素内容). 所以LRUMap进行淘汰时只需要删除链表最后一个即可(即header.after所指的元素对象)

那么那些操作会影响元素的使用情况:

1.       put 当新增加一个集合元素对象,则表示该对象是最近被访问的

2.       get 操作会把当前访问的元素对象作为最近被访问的,会被移到链接表头

注:当执行containsKeycontainsValue操作时,不会影响元素的访问情况。

               LRUMap也是非线程安全。在多线程下使用可通过 Collections.synchronizedMap(Map)操作来保证线程安全。

 

LRUMap的一个简单使用示例:

    public static void main(String[] args) {

       

        LRUMap lruMap = new LRUMap(2);

       

        lruMap.put("a1""1");

        lruMap.put("a2""2");

        lruMap.get("a1");//mark as recent used

        lruMap.put("a3""3");

        System.out.println(lruMap);

    }

               上面的示例,当增加”a3”值时,会淘汰最近最少使用的”a2”, 最后输出的结果为:

                                   {a1=1, a3=3}

 

下面根据LRUMap的源码来解读一下LRUMap的实现原理

整体类图


LRUMap类的关键代码说明如下:

1.       get操作

    public Object get(Object key) {

        LinkEntry entry = (LinkEntry) getEntry(key);

        if (entry == null) {

            return null;

        }

        moveToMRU(entry); //执行LRU操作

        return entry.getValue();

}

moveToMRU方法代码如下:

    protected void moveToMRU(LinkEntry entry) {

        if (entry.after != header) {

            modCount++;

            // remove 从链接中移除当前节点

            entry.before.after = entry.after;

            entry.after.before = entry.before;

            // add first 把节点增加到链接头部

            entry.after = header;

            entry.before = header.before;

            header.before.after = entry;

            header.before = entry;

        } else if (entry == header) {

            throw new IllegalStateException("Can't move header to MRU" +

                " (please report this to commons-dev@jakarta.apache.org)");

        }

    }

2.       put新增操作

  由于继承的AbstractLinkedMap,所以put操作会调用addMapping 方法,完整代码如下:

    protected void addMapping(int hashIndex, int hashCode, Object key, Object value) {

        if (isFull()) {

            LinkEntry reuse = header.after;

            boolean removeLRUEntry = false;

            if (scanUntilRemovable) {

                while (reuse != header && reuse != null) {

                    if (removeLRU(reuse)) {

                        removeLRUEntry = true;

                        break;

                    }

                    reuse = reuse.after;

                }

                if (reuse == null) {

                    throw new IllegalStateException(

                        "Entry.after=null, header.after" + header.after + " header.before" + header.before +

                        " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +

                        " Please check that your keys are immutable, and that you have used synchronization properly."+

                        " If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

                }

            } else {

                removeLRUEntry = removeLRU(reuse);

            }

           

            if (removeLRUEntry) {

                if (reuse == null) {

                    throw new IllegalStateException(

                        "reuse=null, header.after=" + header.after + " header.before" + header.before +

                         " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +

                         " Please check that your keys are immutable, and that you have used synchronization properly." +

                         " If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

                }

                reuseMapping(reuse, hashIndex, hashCode, key, value);

            } else {

                super.addMapping(hashIndex, hashCode, key, value);

            }

        } else {

            super.addMapping(hashIndex, hashCode, key, value);

        }

    }

          当集合的个数超过指定的最大个数时,会调用 reuseMapping函数,把要删除的对象的keyvalue更新为新加入的值,并再次加入到链接表中,并重新排定次序。

reuseMapping函数代码如下:

    protected void reuseMapping(LinkEntry entry, int hashIndex, int hashCode, Object key, Object value) {

        // find the entry before the entry specified in the hash table

        // remember that the parameters (except the first) refer to the new entry,

        // not the old one

        try {

            int removeIndex = hashIndex(entry.hashCodedata.length);

            HashEntry[] tmp = data// may protect against some sync issues

            HashEntry loop = tmp[removeIndex];

            HashEntry previous = null;

            while (loop != entry && loop != null) {

                previous = loop;

                loop = loop.next;

            }

            if (loop == null) {

                throw new IllegalStateException(

                    "Entry.next=null, data[removeIndex]=" + data[removeIndex] + " previous=" + previous +

                    " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +

                    " Please check that your keys are immutable, and that you have used synchronization properly." +

                    " If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

            }

           

            // reuse the entry

            modCount++;

            removeEntry(entry, removeIndex, previous);

            reuseEntry(entry, hashIndex, hashCode, key, value);

            addEntry(entry, hashIndex);

        } catch (NullPointerException ex) {

            throw new IllegalStateException(

                    "NPE, entry=" + entry + " entryIsHeader=" + (entry==header) +

                    " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize +

                    " Please check that your keys are immutable, and that you have used synchronization properly." +

                    " If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

        }

    }

    上面的代码中:

          removeEntry 方法是删除最近最少使用的节点在链接中的引用

    reuseEntry方法则把该节点的keyvalue赋新加入的节点的keyvalue

    addEntry 方法则把该节点加入到链接表,并保障相关的链接顺序

    /**

     * Adds an entry into this map, maintaining insertion order.

     * 

     * This implementation adds the entry to the data storage table and

    * to the end of the linked list.

     *

     * @param entry the entry to add

     * @param hashIndex the index into the data array to store at

     */

    protected void addEntry(HashEntry entry, int hashIndex) {

        LinkEntry link = (LinkEntry) entry;

        link.after = header;

        link.before = header.before;

        header.before.after = link;

        header.before = link;

        data[hashIndex] = entry;

 }

       LRUMap的主要源码实现就解读到这里,实现思路还是比较好理解的。

LRUMap的使用场景会比较多,例如可以很方便的帮我们实现基于内存的 LRU 缓存服务实现。


推荐阅读
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • java解析json转Map前段时间在做json报文处理的时候,写了一个针对不同格式json转map的处理工具方法,总结记录如下:1、单节点单层级、单节点多层级json转mapim ... [详细]
  • DirectShow Filter 开发指南
    本文总结了 DirectShow Filter 的开发经验,重点介绍了 Source Filter、In-Place Transform Filter 和 Render Filter 的实现方法。通过使用 DirectShow 提供的类,可以简化 Filter 的开发过程。 ... [详细]
  • 用示例链接 Java 中的 hashset ... [详细]
  • Redis 是一个高性能的开源键值存储系统,支持多种数据结构。本文将详细介绍 Redis 中的六种底层数据结构及其在对象系统中的应用,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。通过12张图解,帮助读者全面理解 Redis 的数据结构和对象系统。 ... [详细]
  • 在iOS开发中,多线程技术的应用非常广泛,能够高效地执行多个调度任务。本文将重点介绍GCD(Grand Central Dispatch)在多线程开发中的应用,包括其函数和队列的实现细节。 ... [详细]
  • 面试题总结_2019年全网最热门的123个Java并发面试题总结
    面试题总结_2019年全网最热门的123个Java并发面试题总结 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • iOS 百度地图使用指南:基本定位与地理编码
    本文详细介绍如何在 iOS 应用中集成百度地图,实现基本的地图定位和地理编码功能。配置详情请参考官方文档:http://developer.baidu.com/map/index.php?title=iossdk ... [详细]
  • Java 中的控制流与作用域
    本文详细介绍了 Java 中的控制流语句,包括块作用域、if 语句、for 循环、while 循环、do-while 循环、switch 语句以及 break 和 continue 语句的使用方法。通过具体的代码示例,帮助读者更好地理解和应用这些控制流结构。 ... [详细]
  • http:blog.csdn.netzeo112140articledetails7675195使用TCPdump工具,抓TCP数据包。将数据包上传到PC,通过Wireshark查 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • 本文详细介绍了 Pentaho Kettle 中 RowMetaInterface.writeMeta 方法的使用,并提供了多个代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • com.sun.javadoc.PackageDoc.exceptions()方法的使用及代码示例 ... [详细]
  • 多线程基础概览
    本文探讨了多线程的起源及其在现代编程中的重要性。线程的引入是为了增强进程的稳定性,确保一个进程的崩溃不会影响其他进程。而进程的存在则是为了保障操作系统的稳定运行,防止单一应用程序的错误导致整个系统的崩溃。线程作为进程的逻辑单元,多个线程共享同一CPU,需要合理调度以避免资源竞争。 ... [详细]
author-avatar
兄弟连教育
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有