LRU缓存-java简单实现

操作系统中,内存被占满之后还需调入新的内存页,就需要将别内存页进行调出。而决定内存页如何调出的算法被称为页面调度算法,LRU(Least-Recently-Used,最少最近使用)算法是其中一种。它会将最近使用的页面,进行标记,不至于优先被调出至交换区。内存调页算法被广泛用于本地缓存,本文介绍两种简单的实现方法:1、利用HashMap和双向链表实现2、封装JDK中LinkedHashMap实现

1、使用双向链表、两个HashMap

class Node<V> {
	V value;
	public Node<V> last;
	public Node<V> next;
	public Node(V value) {
		this.value = value;
	}
}

class DoubleNodeList<V> {
	private Node<V> head;
	private Node<V> tail;
	
	public void addNode(Node newNode) {
		if (newNode == null) {
			return;
		}
		if (head == null) {
			head = newNode;
			tail = newNode;
		} else {
			tail.next = newNode;
			newNode.last = tail;
			tail = newNode;
		}
	}

	public void moveToTail(Node node) {
		if (tail == null) {
			return;
		}
		if (head == node) {
			head = head.next;
			head.last = null;
		} else {
			node.last.next = node.next;
			node.next.last = node.last;
		}
		node.last = tail;
		node.next = null;
		tail.next = node;
		tail = node;
	}

	public Node<V> removeHead() {
		if (head == null) {
			return null;
		}
		Node<V> res = head;
		if (head == tail) {
			head = null;
			tail = null;
		} else {
			head = res.next;
			res.next = null;
			head.last = null;
		}
		return res;
	}
}

public class LRU<K, V> {
	private java.util.HashMap<K, Node<V>> keyNodeMap;
	private java.util.HashMap<Node<V>, K> nodeKeyMap;
	private DoubleNodeList<V>  list;
	private int size;
	
	public LRU(int size) {
		keyNodeMap = new java.util.HashMap<>();
		nodeKeyMap = new java.util.HashMap<>();
		list = new DoubleNodeList<>();
		this.size = size;
	}

	public void put(K key, V val) {
		if (keyNodeMap.containsKey(key)) {
			Node<V> node = keyNodeMap.get(key);
			node.value = val;
			keyNodeMap.put(key, node);
			nodeKeyMap.put(node, key);
			list.moveToTail(node);
		} else {
			Node<V> newNode = new Node(val);
			keyNodeMap.put(key, newNode);
			nodeKeyMap.put(newNode, key);
			list.addNode(newNode);
			if (keyNodeMap.size() == size + 1) {
				deleteNode();
			}
		}
	}

	public void deleteNode() {
		Node<V> delNode = list.removeHead();
		Key key = nodeKeyMap.get(delNode);
		keyNodeMap.remove(key);
		nodeKeyMap.remove(delNode);
	}

	public V get(K key) {
		if (keyNodeMap.containsKey(key)) {
			Node<V> node = keyNodeMap.get(key);
			list.moveToTail(node);
			return node.value;
		}
		return null;
	}

	public static void main(String[] args) {
		LRU<String, Integer> cache = new LRU<String, Integer>();
		cache.put("A", 1);
		cache.put("B", 2);
		cache.put("C", 3);
		cache.get("A");
		cache.put("D", 4);
		System.out.println("B" + " : " + cache.get("B")); // 可以实现Iterable接口,实现遍历
		System.out.println("A" + " : " + cache.get("A"));
		System.out.println("C" + " : " + cache.get("C"));
		System.out.println("D" + " : " + cache.get("D"));
}

2、封装JDK1.8中LinkedHashMap实现,进行代码重用

public class LRUExtendsLinkedHashMap<K,V> extends java.util.LinkedHashMap<K,V> {

	private int maxCapacity;
	
	public LRUExtendsLinkedHashMap() {
		
	}
	// LinkedHashMap 有个构造方法带accessOrder参数,这个参数可以在访问元素时,将元素移动到将要删除元素的另一端
	public LRUExtendsLinkedHashMap(int max,float loadFactor, boolean accessOrder) {
		super(3, loadFactor, accessOrder);
		maxCapacity = max;
	}
	
	private static final long serialVersionUID = -3892648982214745509L;
	
	@Override
	protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
		return size() > maxCapacity;
	}

	public void test() {
		LRUExtendsLinkedHashMap<String,Integer> cache = new LRUExtendsLinkedHashMap<String,Integer>(3, 0.75F,true);
		cache.put("A", 1);
		cache.put("B", 2);
		cache.put("C", 3);
		cache.get("A");
		cache.put("D", 4);
		for (java.util.Map.Entry<String, Integer> entry : cache.entrySet()) {
			String key = entry.getKey();
			Integer val = entry.getValue();
			System.out.println(key + " : " + val.intValue());
		}
	}

	
	public static void main(String[] args) {
		new LRUExtendsLinkedHashMap().test();
	}
}

LRU缓存的缺点是每次访问元素,需要移动链表中节点以在调出节点都在头部进行,这会占用CPU时间;Linux内核中提出了这样的办法:CPU中使用一个计数器,每一次CPU周期,计数器的值自增,当访问LRU缓存中的元素时,将CPU计数器值赋给链表节点维护的计数器值,每次进行调页时,只需遍历链表,找出节点中计数值最小的节点调出到交换区,就不需要每次都移动链表。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值