1. 程式人生 > >模仿LinkedHashMap來進行LRU演算法

模仿LinkedHashMap來進行LRU演算法

讓我們以使用者資訊的需求為例,來演示一下LRU演算法的基本思路:

 

1.假設我們使用雜湊連結串列來快取使用者資訊,目前快取了4個使用者,這4個使用者是按照時間順序依次從連結串列右端插入的。

2.此時,業務方訪問使用者5,由於雜湊連結串列中沒有使用者5的資料,我們從資料庫中讀取出來,插入到快取當中。這時候,連結串列中最右端是最新訪問到的使用者5,最左端是最近最少訪問的使用者1。

 

 

3.接下來,業務方訪問使用者2,雜湊連結串列中存在使用者2的資料,我們怎麼做呢?我們把使用者2從它的前驅節點和後繼節點之間移除,重新插入到連結串列最右端。這時候,連結串列中最右端變成了最新訪問到的使用者2,最左端仍然是最近最少訪問的使用者1。

 

4.接下來,業務方請求修改使用者4的資訊。同樣道理,我們把使用者4從原來的位置移動到連結串列最右側,並把使用者資訊的值更新。這時候,連結串列中最右端是最新訪問到的使用者4,最左端仍然是最近最少訪問的使用者1。

 

5.後來業務方換口味了,訪問使用者6,使用者6在快取裡沒有,需要插入到雜湊連結串列。假設這時候快取容量已經達到上限,必須先刪除最近最少訪問的資料,那麼位於雜湊連結串列最左端的使用者1就會被刪除掉,然後再把使用者6插入到最右端。

 

以上,就是LRU演算法的基本思路。

import java.util.HashMap;

public class TestLRU {
	//頭
	private Node head;
	//尾
	private Node end;
	// 快取儲存上限
	private int limit;
	private HashMap<String, Node> hashMap;

	public TestLRU(int limit) {
		this.limit = limit;
		hashMap = new HashMap<String, Node>();
	}

	public String get(String key) {
		Node node = hashMap.get(key);
		if (node == null) {
			return null;
		}
		refreshNode(node);
		return node.value;
	}

	public void put(String key, String value) {
		Node node = hashMap.get(key);
		if (node == null) {
			// 如果key不存在,插入key-value
			if (hashMap.size() >= limit) {
				String oldKey = removeNode(head);
				hashMap.remove(oldKey);
			}
			node = new Node(key, value);
			addNode(node);
			hashMap.put(key, node);
		} else {
			// 如果key存在,重新整理key-value
			node.value = value;
			refreshNode(node);
		}
	}

	public void remove(String key) {
		Node node = hashMap.get(key);
		removeNode(node);
		hashMap.remove(key);
	}

	/**
	 * 重新整理被訪問的節點位置
	 * 
	 * @param node 被訪問的節點
	 */
	private void refreshNode(Node node) {
		// 如果訪問的是尾節點,無需移動節點
		if (node == end) {
			return;
		}
		// 移除節點
		removeNode(node);
		// 重新插入節點
		addNode(node);
	}

	/**
	 * 刪除節點
	 * 
	 * @param node 要刪除的節點
	 */

	private String removeNode(Node node) {
		if (node == end) {
			// 移除尾節點
			end = end.pre;
		} else if (node == head) {
			// 移除頭節點
			head = head.next;
		} else {
			// 移除中間節點
			node.pre.next = node.next;
			node.next.pre = node.pre;
		}
		return node.key;
	}

	/**
	 * 尾部插入節點
	 * 
	 * @param node 要插入的節點
	 */
	private void addNode(Node node) {
		if (end != null) {
			end.next = node;
			node.pre = end;
			node.next = null;
		}
		end = node;
		//第一個節點
		if (head == null) {
			head = node;
		}
	}

	class Node {
		Node(String key, String value) {
			this.key = key;
			this.value = value;
		}

		public Node pre;
		public Node next;
		public String key;
		public String value;
	}

	public static void main(String[] args) {

		TestLRU lruCache = new TestLRU(5);
		lruCache.put("001", "使用者1資訊");
		lruCache.put("002", "使用者2資訊");
		lruCache.put("003", "使用者3資訊");
		lruCache.put("004", "使用者4資訊");
		lruCache.put("005", "使用者5資訊");
		lruCache.get("001");
		lruCache.put("004", "使用者4資訊更新");
		lruCache.put("006", "使用者6資訊");

		System.out.println(lruCache.get("001"));
		System.out.println(lruCache.get("002"));
		System.out.println(lruCache.get("006"));
	}
}