LRU算法
Posted zmycoco2
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LRU算法相关的知识,希望对你有一定的参考价值。
LRU是Least Recently Used 的缩写,即“最近最少使用”,基于LRU算法实现的Cache机制简单地说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现就是将最老的数据删除。Java里面实现LRU缓存通常有两种选择,一种是使用LinkedHashMap,一种是自己设计数据结构,使用链表+HashMap方式。
LinkedHashMap自身已经实现了顺序存储,默认情况下是按照元素的添加顺序存储,也可以启用按照访问顺序存储,即最近读取的数据放在最前面,最早读取的数据放在最后面,然后它还有一个判断是否删除最老数据的方法,默认是返回false,即不删除数据。
代码清单4-4所示示例是LinkedHashMap的一个构造函数,当参数accessOrder为true时,将会按照访问顺序排序,最后访问的放在最前,最早访问的放在后面。
代码清单4-4 LRU Cache的LinkedHashMap实现
public LinkedHashMap(int initialCapacity, float loadFactor, booleanaccessOrder)
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
代码清单4-5所示赛马是LinkedHashMap自带的判断方法,判断是否删除最老的元素方法,默认返回false,即不删除老数据,我们要做的就是重写这个方法,当满足一定条件时删除老数据。
代码清单4-5 重写删除方法
protected booleanremoveEldestEntry(Map.Entry<K,V> eldest)
return false;
采用inheritance方式实现比较简单,该方式实现了Map接口,在多线程环境使用时可以使用Collections.synchronizedMap()方法实现线程安全操作。
代码清单4-6 LRU缓存LinkedHashMap(inheritance)实现
importjava.util.LinkedHashMap;
import java.util.Map;
public classLRUCache2<K, V> extends LinkedHashMap<K, V>
private final int MAX_CACHE_SIZE;
public LRUCache2(int cacheSize)
super((int) Math.ceil(cacheSize / 0.75)+ 1, 0.75f, true);
MAX_CACHE_SIZE = cacheSize;
@Override
protected booleanremoveEldestEntry(Map.Entry eldest)
return size() > MAX_CACHE_SIZE;
@Override
public String toString()
StringBuilder sb = new StringBuilder();
for (Map.Entry<K, V> entry :entrySet())
sb.append(String.format("%s:%s", entry.getKey(), entry.getValue()));
return sb.toString();
代码清单4-6的实现是比较标准的实现,在实际使用过程中这样写还是有些烦琐,更实用的方法是像代码清单4-7这样写,省去了单独新建一个类的麻烦。
代码清单4-7 LRU缓存LinkedHashMap(inheritance)实现改进版
final int cacheSize =100;
Map<String,String> map = new LinkedHashMap<String, String>((int) Math.ceil(cacheSize/ 0.75f) + 1, 0.75f, true)
@Override
protected booleanremoveEldestEntry(Map.Entry<String, String> eldest)
return size() > cacheSize;
;
相比inheritance实现方式来说,delegation实现方式实现更加优雅一些,但是由于没有实现Map接口,所以线程同步就需要自己搞定了。
代码清单4-8 LRU缓存LinkedHashMap(delegation)实现
importjava.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Created by liuzhao on 14-5-13.
*/
public classLRUCache3<K, V>
private final int MAX_CACHE_SIZE;
private final float DEFAULT_LOAD_FACTOR =0.75f;
LinkedHashMap<K, V> map;
public LRUCache3(int cacheSize)
MAX_CACHE_SIZE = cacheSize;
//根据cacheSize和加载因子计算hashmap的capactiy,+1确保当达到cacheSize上限时不会触发hashmap的扩容,
int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTOR) + 1;
map = new LinkedHashMap(capacity,DEFAULT_LOAD_FACTOR, true)
@Override
protected booleanremoveEldestEntry(Map.Entry eldest)
return size() >MAX_CACHE_SIZE;
;
public synchronized void put(K key, Vvalue)
map.put(key, value);
public synchronized V get(K key)
return map.get(key);
public synchronized void remove(K key)
map.remove(key);
public synchronized Set<Map.Entry<K,V>> getAll()
return map.entrySet();
public synchronized int size()
return map.size();
public synchronized void clear()
map.clear();
@Override
public String toString()
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : map.entrySet())
sb.append(String.format("%s:%s", entry.getKey(), entry.getValue()));
return sb.toString();
注意,上面的实现方式是非线程安全的,若在多线程环境下使用需要在相关方法上添加synchronized以实现线程安全操作。
前面说过,除了LinkedHashMap方式以外,我们还有一种采用LRU Cache的链表+HashMap实现的方式,如代码清单4-9包含的代码所示。
代码清单4-9 LRU Cache的链表+HashMap实现
importjava.util.HashMap;
public classLRUCache1<K, V>
private final int MAX_CACHE_SIZE;
private Entry first;
private Entry last;
private HashMap<K, Entry<K, V>>hashMap;
public LRUCache1(int cacheSize)
MAX_CACHE_SIZE = cacheSize;
hashMap = new HashMap<K, Entry<K,V>>();
public void put(K key, V value)
Entry entry = getEntry(key);
if (entry == null)
if (hashMap.size() >=MAX_CACHE_SIZE)
hashMap.remove(last.key);
removeLast();
entry = new Entry();
entry.key = key;
entry.value = value;
moveToFirst(entry);
hashMap.put(key, entry);
public V get(K key)
Entry<K, V> entry =getEntry(key);
if (entry == null) return null;
moveToFirst(entry);
return entry.value;
public void remove(K key)
Entry entry = getEntry(key);
if (entry != null)
if (entry.pre != null)entry.pre.next = entry.next;
if (entry.next != null)entry.next.pre = entry.pre;
if (entry == first) first =entry.next;
if (entry == last) last =entry.pre;
hashMap.remove(key);
private void moveToFirst(Entry entry)
if (entry == first) return;
if (entry.pre != null) entry.pre.next =entry.next;
if (entry.next != null) entry.next.pre= entry.pre;
if (entry == last) last = last.pre;
if (first == null || last == null)
first = last = entry;
return;
entry.next = first;
first.pre = entry;
first = entry;
entry.pre = null;
private void removeLast()
if (last != null)
last = last.pre;
if (last == null) first = null;
else last.next = null;
private Entry<K, V> getEntry(K key)
return hashMap.get(key);
@Override
public String toString()
StringBuilder sb = new StringBuilder();
Entry entry = first;
while (entry != null)
sb.append(String.format("%s:%s", entry.key, entry.value));
entry = entry.next;
return sb.toString();
class Entry<K, V>
public Entry pre;
public Entry next;
public K key;
public V value;
欢迎关注麦克叔叔每晚十点说,让我们一起交流与学习。
以上是关于LRU算法的主要内容,如果未能解决你的问题,请参考以下文章