分析LinkedHashMap源码的LRU实现

一、前言

前段时间研究了memcached,而且操作系统的课程也刚刚完成,在两个里面多次出现LRU(last recently used最近最少使用)算法,虽然思想很简单。但是还是值得我们研究,无意间在看LinkedHashMap的源码的时候看见貌似这个类里面有默认的LRU实现。我们现在就来分析一下他的源代码

 /**
     * Returns <tt>true</tt> if this map should remove its eldest entry.
     * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
     * inserting a new entry into the map.  It provides the implementor
     * with the opportunity to remove the eldest entry each time a new one
     * is added.  This is useful if the map represents a cache: it allows
     * the map to reduce memory consumption by deleting stale entries.
     *
     * <p>Sample use: this override will allow the map to grow up to 100
     * entries and then delete the eldest entry each time a new entry is
     * added, maintaining a steady state of 100 entries.
     * <pre>
     *     private static final int MAX_ENTRIES = 100;
     *
     *     protected boolean removeEldestEntry(Map.Entry eldest) {
     *        return size() > MAX_ENTRIES;
     *     }
     * </pre>
     *
     * <p>This method typically does not modify the map in any way,
     * instead allowing the map to modify itself as directed by its
     * return value.  It <i>is</i> permitted for this method to modify
     * the map directly, but if it does so, it <i>must</i> return
     * <tt>false</tt> (indicating that the map should not attempt any
     * further modification).  The effects of returning <tt>true</tt>
     * after modifying the map from within this method are unspecified.
     *
     * <p>This implementation merely returns <tt>false</tt> (so that this
     * map acts like a normal map - the eldest element is never removed).
     *
     * @param    eldest The least recently inserted entry in the map, or if
     *           this is an access-ordered map, the least recently accessed
     *           entry.  This is the entry that will be removed it this
     *           method returns <tt>true</tt>.  If the map was empty prior
     *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
     *           in this invocation, this will be the entry that was just
     *           inserted; in other words, if the map contains a single
     *           entry, the eldest entry is also the newest.
     * @return   <tt>true</tt> if the eldest entry should be removed
     *           from the map; <tt>false</tt> if it should be retained.
     */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;//返回false代表不会删除map会自动扩容,返回true代表会删除
    }

很明显,上一个函数写得很清楚,该函数数默认返回false的,代表linkedhashmap会自动的进行扩容操作,如果返回true的话map则会删掉排在最后的一个元素(linkedhashmap是有序的)我们可以来看看addENtry函数是怎样的

/**
     * This override alters behavior of superclass put method. It causes newly
     * allocated entry to get inserted at the end of the linked list and
     * removes the eldest entry if appropriate.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        createEntry(hash, key, value, bucketIndex);

        // Remove eldest entry if instructed, else grow capacity if appropriate
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {//返回true,删掉entry
            removeEntryForKey(eldest.key);
        } else {//否则自动两倍扩容
            if (size >= threshold)
                resize(2 * table.length);
        }
    }

那么现在就剩下排序的问题了,大家都知道LRU的原理就是把最近使用的排在前面,最少使用的排在后面(addENTRY时会删除多余的元素),那么linkedhashmap是在什么时候开始为最近使用的排序呢?很明显我们要知道他什么时候使用就应该是i我们调用get方法的时候,那我们现在来看看get方法

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     */
    public V get(Object key) {
        Entry<K,V> e = (Entry<K,V>)getEntry(key);//取得entry,
        if (e == null)
            return null;
        e.recordAccess(this);//这个方法很重要
        return e.value;
    }

还有一点笔者必须要提醒大家,我们传入参数的时候有个这个参数accessOrder,顾名思义,他是为了排序而生。true代表排序。false反之

 /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> for insertion-order.
     *
     * @serial
     */
    private final boolean accessOrder;

我们可以看到。当我们执行get方法的时候会调用一个recordAccess的方法传入this变量。ok,他应该是把操作交给entry类了吧。好吧我们来看看entry的源代码

 /**
     * LinkedHashMap entry.
     */
    private static class Entry<K,V> extends HashMap.Entry<K,V> {
        // These fields comprise the doubly linked list used for iteration.
        Entry<K,V> before, after;

        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }

        /**
         * Removes this entry from the linked list.
         */
        private void remove() {//删除该entry
            before.after = after;
            after.before = before;
        }

        /**
         * Inserts this entry before the specified existing entry in the list.//加入到头部
         */
        private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        /**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         */
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {//得到传过来的linkedhashmap的order值。如果是true这进行一下操作,先执行remove()接着执行addbefore()
                lm.modCount++;//根据方法名字大家都应该恍然大悟了吧
                remove();
                addBefore(lm.header);
            }
        }

        void recordRemoval(HashMap<K,V> m) {
            remove();
        }
    }

ok!linkedHashmap的Lru实现就是这么简单,但是个人认为不算很完美的LRU,大家有没有看到一个弊端,只要调用一次就跑到最前面去了,我觉得这不是一个很好的实现。

下面给出一个自己实现的一个简单的LRUCache类

package algori;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
 *  实现LRU算法
 * @author xiezhaodong
 *
 * @param <K>
 * @param <V>
 */
public class LRUCache <K,V> extends LinkedHashMap<K, V>{
	private static final int DEFAULT_CAPACITY=100;

	private static final float DEFAULT_FACTOR=0.75f;

	private int REAL_CAPACOTY;//记录真实容量
	public LRUCache(int initialCapacity, float loadFactor){
		super(initialCapacity, loadFactor, true);
		this.REAL_CAPACOTY=initialCapacity;
	}

	public LRUCache() {
		this(DEFAULT_CAPACITY,DEFAULT_FACTOR);
	}

	//覆盖父类方法,超过容量以后去掉LRU中的数据
	@Override
	protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
		return size()>REAL_CAPACOTY;
	}

	public synchronized V getCache (K key) {
	   return get(key); }  

	public synchronized void putCache(K key, V value) {
	  put (key, value); }  

	public synchronized void clear() {
	   clear(); }  

	public synchronized int usedEntries() {
	   return size(); }  

	public synchronized Collection<Map.Entry<K,V>> getAll() {
	   return new ArrayList<Map.Entry<K,V>>(entrySet());
	   }  

	}

转载请注明http://blog.csdn.net/a837199685/article

时间: 2024-11-04 22:08:31

分析LinkedHashMap源码的LRU实现的相关文章

LinkedHashMap源码及LRU算法应用

先来说说它的特点,然后在一一通过分析源码来验证其实现原理 1.能够保证插入元素的顺序.深入一点讲,有两种迭代元素的方式,一种是按照插入元素时的顺序迭代,比如,插入A,B,C,那么迭代也是A,B,C,另一种是按照访问顺序,比如,在迭代前,访问了B,那么迭代的顺序就是A,C,B,比如在迭代前,访问了B,接着又访问了A,那么迭代顺序为C,B,A,比如,在迭代前访问了B,接着又访问了B,然后在访问了A,迭代顺序还是C,B,A.要说明的意思就是不是近期访问的次数最多,就放最后面迭代,而是看迭代前被访问的时

LinkedHashMap 源码分析

LinkedHashMap 源码分析 1. 基本结构 1. 实现 实现的接口是 Map 2. 继承 ?? 继承的是 HashMap 这个就比较熟悉了,事实上我们会看到 LinkedHashMap 代码量非常的少,主要就是因为他继承的 HashMap ,继承了大多数的操作. 仔细一点的都会发现 HashMap 里面有非常多的空白方法,这些方法其实是模板方法,为了让继承 HashMap 的类重写一些自己的特性.而不破坏代码结构. 3. 数据域 1. 基本字段 ?? 在 HashMap 的基础上他添加

LRU、FIFO缓存实现以及LinkedHashMap源码

本篇将描述如何使用LinkedHashMap实现LRU以及FIFO缓存,并将从LinkedHashMap源码层面描述是如何实现这两种缓存的. 1.缓存描述 首先介绍一下FIFO.LRU两种缓存: FIFO(First In First out):先见先出,淘汰最先近来的页面,新进来的页面最迟被淘汰,完全符合队列. LRU(Least recently used):最近最少使用,淘汰最近不使用的页面. 2.代码实现 以下是通过LinkedHashMap实现两种缓存. public class Ca

LRU算法实现,HashMap与LinkedHashMap源码的部分总结

关于HashMap与LinkedHashMap源码的一些总结 JDK1.8之后的HashMap底层结构中,在数组(Node<K,V> table)长度大于64的时候且链表(依然是Node)长度大于8的时候,链表在转换为红黑树时,链表长度小于等于6时将不会进行转化为红黑树.目的是为了保证效率.其中链表的结点只有next,LinkedHashMap是在Entry<K,V>中添加before, after(双向链表的定义),保证可迭代,遍历时为存入顺序. 下面是LinkedHashMap

【Java集合源码剖析】LinkedHashmap源码剖析

LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同. LinkedHashMap可以用来实现LRU算法(这会在下面的源码中进行分析). LinkedHashMap同样是非线程安全的,只在单线程环境下使用. LinkedHashMap源码剖析 LinkedHashM

转:【Java集合源码剖析】LinkedHashmap源码剖析

转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985   前言:有网友建议分析下LinkedHashMap的源码,于是花了一晚上时间研究了下,分享出此文(这个系列的最后一篇博文了),希望大家相互学习.LinkedHashMap的源码理解起来也不难(当然,要建立在对HashMap源码有较好理解的基础上). LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加

【源码】LinkedHashMap源码剖析

注:以下源码基于jdk1.7.0_11 之前的两篇文章通过源码分析了两种常见的Map集合,HashMap和Hashtable.本文将继续介绍另一种Map集合--LinkedHashMap. 顾名思义,LinkedHashMap除了是一个HashMap之外,还带有LinkedList的特点,也就是说能够保持遍历的顺序和插入的顺序一致,那么它是怎么做到的呢?下面我们开始分析. 首先看构造器. public class LinkedHashMap<K,V> extends HashMap<K,

OpenStack_Swift源码分析——Object-auditor源码分析(2)

1 Object-aduitor审计具体分析 上一篇文章中,讲解了Object-aduitor的启动,其中审计的具体执行是AuditorWorker实现的,在run_audit中实例化了AuditorWorker类,并调用audit_all_objects方法,下面看此方法的具体代码实现: def audit_all_objects(self, mode='once', device_dirs=None): #run_forever传过来的mode 为forever description =

OpenStack_Swift源码分析——Object-auditor源码分析(1)

1 Object-auditor 的启动 Object-auditor的启动和object-replicator的启动过程是一样的,首先是执行启动脚本 swift-init object-auditor start 启动脚本会运行swift源码bin目录下的swift-ojbect-auditor if __name__ == '__main__': parser = OptionParser("%prog CONFIG [options]") parser.add_option('-