2020/4/1 更新
昨晚做了一下携程的19年笔试题,遇到了LRU。今天有一场携程的笔试,晚上等笔试完来更新一下对LRU的讨论。
LRU https://www.cnblogs.com/NickyYe/p/4460239.html
https://my.oschina.net/u/1540325/blog/603413
首先,什么是LRU?(解释摘自百度)
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
常见的可以用Java自带的LinkedHashMap,LinkedList(或者ArrayDeque)和数组实现。
作为一道常见的面试题,记录一下这几种解法。
- LinkedHashMap,是一种有序的Map,它可以按照插入顺序或者访问顺序进行排序。而TreeMap,是按照键的自然排序或者自定义排序来进行排序。为了实现LRU,采用访问顺序的LinkedHashMap,LinkedHashMap提供了一个钩子方法,在新插入元素后可以决定是否删除最老的元素。
public class LRUcache<K,V> extends LinkedHashMap<K, V> {
private int capacity;
LRUcache(int capacity){
super(capacity+1,1.0f,true);
//调用父类的构造方法,初始化时指定
// capacity最大容量,
// loadfactor装载因子,
//accessOrder 是否按访问排序,默认是false
this.capacity=capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size()>this.capacity;
}
}
- 使用LinkedList(或者ArrayDeque)和HashSet实现,这里其实LinkedList(或者ArrayDeque)是自带contains()函数的,但是时间复杂度为O(n),因此使用HashSet将查找时间复杂度降为O(1)。这里还要特别注意的一点是LinkedList和ArrayDeque的remove方法的区别。
在ArrayDeque中,
public boolean remove(Object o) {
return removeFirstOccurrence(o);
}
在LinkedList中,remove方法进行了重载,在实现LRU过程中remove时记得使用remove(new Integer(n))而不是remove(n),
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
下面看实现的代码。
public class LRUcache2 {
private LinkedList<Integer> cacheList = new LinkedList<Integer>();
private HashSet<Integer> set =new HashSet<>();
private int capacity;
LRUcache2(int capacity){
this.capacity=capacity;
}
private boolean isQueueFull(){
return cacheList.size()==capacity;
}
//入队函数
public void enqueue(int n){
if(isQueueFull()){
Integer removePage = cacheList.removeLast();
set.remove(removePage);
}
cacheList.addFirst(n);
set.add(n);
}
//当某个页被访问到时
public void accessPage(int n){
//不在缓存中则入队
if(!set.contains(n)){
enqueue(n);
}
//在缓存中,检查是否在队头,不在则移除元素并添加到队头
else if(!cacheList.getFirst().equals(n))
{
cacheList.remove(new Integer(n));
cacheList.addFirst(n);
}
}
public static void main(String[] args) {
LRUcache2 lru =new LRUcache2(3);
lru.accessPage(1);
lru.accessPage(2);
lru.accessPage(5);
lru.accessPage(1);
lru.accessPage(6);
lru.accessPage(7);
System.out.println();//断点调试 结果7,6,1
}
}
- 数组实现,有时间再写吧.....