LRU算法简单实现

 什么是LRU

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”

距离现在最早使用的会被我们替换掉。不够形象的话我们看下面的例子。

size=3的缓存淘汰实现:

 

在插入元素1的时候,位置1、2、3依次为1、null、null

在插入元素2的时候,位置1、2、3依次为1、2、null

....

在插入元素4的时候,位置1、2、3依次为2、3、4,本来位置1的元素1被淘汰

利用LinkedHashMap实现的简单LRU

import java.util.LinkedHashMap;
import java.util.Map;

public class UseLinkedHashMapCache<K,V> extends LinkedHashMap<K,V> {

    private final int cacheSize;
    /**
     * 传递进来最多能缓存多少数据
     *
     * @param cacheSize 缓存大小
     */
    public UseLinkedHashMapCache(int cacheSize) {
        // true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
        super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
        this.cacheSize = cacheSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
        return size() > cacheSize;  
    }
}

 

这里需要注意初始容量大小:

 //缓存最大容量 => initialCapacity * DEFAULT_LOAD_FACTOR,避免扩容操作
 int initialCapacity = (int) Math.ceil(maxCacheSie / DEFAULT_LOAD_FACTOR) + 1;

看一下LinkedHashMap的构造方法:

    /**
     * Constructs an empty <tt>LinkedHashMap</tt> instance with the
     * specified initial capacity, load factor and ordering mode.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

accessOrder有两种模式,如果设置为true的话是按照访问排序,设置是false的话是按照插入顺序排序

然后在看一下我们重写的removeEldestEntry方法:

    /**
     * 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() &gt; 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;
    }

在每次插入元素(put或者putAll)会调用该方法,如果返回true的话,则会删除该map的最早插入的那个元素。所以当这个map要作为缓存的时候,在map存储不下更多的元素时候,把最老的那个元素删除来减少内存消耗

测试

public class TestLRU {

        public static void main(String[] args) {
            UseLinkedHashMapCache<Integer,String> cache = new UseLinkedHashMapCache<>(4);
            cache.put(1, "one");
            cache.put(2, "two");
            cache.put(3, "three");
            cache.put(4, "four");
            cache.put(2, "two");for (Integer key : cache.keySet()) {
                System.out.println("Key:\t"+key);
            }
        }
}

输出结果(按照访问顺序来淘汰):

Key:    1
Key:    3
Key:    4
Key:    2

然后把之前构造方法中的accessOrder模式改为false(也就是按插入顺序来淘汰)

输出结果:

Key:    1
Key:    2
Key:    3
Key:    4

 

 

整理自:

https://blog.csdn.net/nakiri_arisu/article/details/79205660

https://blog.csdn.net/u012485480/article/details/82427037

posted @ 2020-03-06 14:15  sw_kong  阅读(322)  评论(0编辑  收藏  举报