1. 程式人生 > >如何用LinkedHashMap實現LRU快取演算法

如何用LinkedHashMap實現LRU快取演算法

阿里巴巴筆試考到了LRU,一激動忘了怎麼回事了。。準備不充分啊。。

快取這個東西就是為了提高執行速度的,由於快取是在寸土寸金的記憶體裡面,不是在硬盤裡面,所以容量是很有限的。LRU這個演算法就是把最近一次使用時間離現在時間最遠的資料刪除掉。先說說List:每次訪問一個元素後把這個元素放在 List一端,這樣一來最遠使用的元素自然就被放到List的另一端。快取滿了t的時候就把那最遠使用的元素remove掉。但更實用的是HashMap。因為List太慢,要刪掉的資料總是位於List底層陣列的第一個位置,刪掉之後,後面的資料要向前補位。。所以複雜度是O(n),那就用連結串列結構的LinkedHashMap唄~,LinkedHashMap預設的元素順序是put的順序,但是如果使用帶引數的建構函式,那麼LinkedHashMap會根據訪問順序來調整內部 順序。 LinkedHashMap的get()方法除了返回元素之外還可以把被訪問的元素放到連結串列的底端,這樣一來每次頂端的元素就是remove的元素。

建構函式如下:

public LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder);

 initialCapacity   初始容量

 loadFactor    載入因子,一般是 0.75f

accessOrder   false 基於插入順序  true  基於訪問順序(get一個元素後,這個元素被加到最後,使用了LRU  最近最少被使用的排程演算法)

來個例子吧:
import java.util.*;

class Test
{
	public static void main(String[] args) throws Exception{
	
		Map<Integer,Integer> map=new LinkedHashMap<>(10,0.75f,true);
		map.put(9,3);
		map.put(7,4);
		map.put(5,9);
		map.put(3,4);
		//現在遍歷的話順序肯定是9,7,5,3
		//下面訪問了一下9,3這個鍵值對,輸出順序就變嘍~
		map.get(9);
		for(Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();it.hasNext();){
			System.out.println(it.next().getKey());
		}
	}
}
輸出 7
5
3
9
好玩吧~ 下面開始實現LRU快取嘍~
import java.util.*;
//擴充套件一下LinkedHashMap這個類,讓他實現LRU演算法
class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{
	//定義快取的容量
	private int capacity;
	private static final long serialVersionUID = 1L;
	//帶引數的構造器	
	LRULinkedHashMap(int capacity){
		//呼叫LinkedHashMap的構造器,傳入以下引數
		super(16,0.75f,true);
		//傳入指定的快取最大容量
		this.capacity=capacity;
	}
	//實現LRU的關鍵方法,如果map裡面的元素個數大於了快取最大容量,則刪除連結串列的頂端元素
	@Override
	public boolean removeEldestEntry(Map.Entry<K, V> eldest){ 
		System.out.println(eldest.getKey() + "=" + eldest.getValue());  
		return size()>capacity;
	}  
}
//測試類
class Test{
public static void main(String[] args) throws Exception{

	//指定快取最大容量為4
	Map<Integer,Integer> map=new LRULinkedHashMap<>(4);
	map.put(9,3);
	map.put(7,4);
	map.put(5,9);
	map.put(3,4);
	map.put(6,6);
	//總共put了5個元素,超過了指定的快取最大容量
	//遍歷結果
		for(Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();it.hasNext();){
			System.out.println(it.next().getKey());
		}
	}
}
輸出結果如下 9=3
9=3
9=3
9=3
9=3
7
5
3
6
分析一下:使用帶引數構造器,且啟用LRU模式的LinkedHashMap會在每次有新元素加入的時候,判斷當前儲存元素是否超過了快取上限,也就是執行 一次removeEldestEntry方法,看最後的遍歷結果,發現果然把9刪除了,LRU發揮作用了~