手撕LRU算法

LRU(Least Recently Used)最近最少使用,是一种页面置换算法,自认为这个翻译有点不太贴切。LRU通俗来说就是在一块大小有限的内存中,最近访问的(包括增改查)数据放到最前面,当加入新的数据时,如果已满,则需要先删除最后面的数据。所以就是将最近使用的放到最前面。

hashmap+双向链表实现

import java.util.HashMap;
import java.util.Map;

public class LRUCache {
	
	// 定义一个内部节点类
	class LNode{
		int key;   // 用以删除hashmap中对应的节点
		int value;  // 数据
		LNode pre;
		LNode next;
	}
	/*
	 * 在头节点后面增加新节点
	 */
	private void addNode(LNode node) {
		head.next.pre = node;
		node.next = head.next;
		head.next = node;
		node.pre = head;
	}
	/*
	 * 删除一个节点
	 */
	private void removeNode(LNode node) {
		LNode node1 = node.pre;
		LNode node2 = node.next;
		
		node1.next = node2;
		node2.pre = node1;
	}
	/*
	 * 将节点移动到头部
	 */
	private void moveToHead(LNode node) {
		removeNode(node);
		addNode(node);
	}
	
	private LNode head;  // 头节点
	private LNode tail;  // 尾节点
	private int size;  // 占用量
	private int capacity;  // 容量
	private Map<Integer, LNode> cache = new HashMap<Integer, LRUCache.LNode>();
	
	/*
	 * 构造函数
	 */
	public LRUCache(int capacity) {
		this.capacity = capacity;
		this.size = 0;
		
		head = new LNode();
		tail = new LNode();
		head.next = tail;
		tail.pre = head;
	}
	
	/*
	 * 获取内存占用量
	 */
	public int getSize() {
		return this.size;
	}
	
	/*
	 * 获取数据
	 */
	public int get(int key) {
		LNode node = cache.get(key);
		
		if(node == null) {  // 空
			return Integer.MIN_VALUE;
		}
		moveToHead(node);  // 将访问的节点移动到最前面
		return node.value;
	}
	
	/*
	 * 修改数据,如不存在,则添加数据
	 */
	public void put(int key, int value) {
		LNode node = cache.get(key);
		
		if(node == null) {  // 增加
			LNode newNode = new LNode();
			newNode.key = key;
			newNode.value = value;
			if(size == capacity) {  // 容量已用完
				// 删除队尾节点
				cache.remove(tail.pre.key);  
				removeNode(tail.pre);
				size--;
			}
			// 添加新数据
			addNode(newNode);
			cache.put(key, newNode);
			size++;
		}
		else {  // 修改
			node.value = value;
			moveToHead(node);
		}
	}
	
	/*
	 * 删除数据
	 */
	public void remove(int key) {
		LNode node = cache.get(key);
		
		if(node == null) {
			return;
		}
		
		removeNode(node);
		cache.remove(key);
	}
	
	/*
	 * 重写toString,为了测试
	 */
	public String toString() {
		StringBuilder strB = new StringBuilder();
		strB.append("{");
		LNode node = head.next;
		while(node != tail) {
			strB.append(node.key +":"+ node.value + ", ");
			node = node.next;
		}
		if(strB.length() > 1) {  // 将最后一个逗号及空格删除
			strB.delete(strB.length()-2, strB.length()-1);
		}
		strB.append("}");
		return strB.toString();
	}
	
	public static void main(String[] args) {  // 测试
		LRUCache lru = new LRUCache(3);
		lru.put(1, 5);  // 增加
		lru.put(2, 2);
		lru.put(3, 6);  
		System.out.println("增加:" + lru.toString());
		lru.put(1, 3);  // 修改
		System.out.println("修改:" + lru.toString());
		lru.put(4, 0);  // 过量增加
		System.out.println("过量增加:" + lru.toString());
		System.out.println("获取:" + lru.get(1));
		System.out.println("获取:" + lru.toString());
		lru.remove(4);  // 删除
		System.out.println("删除:" + lru.toString());
	}
}

运行结果

增加:{3:6, 2:2, 1:5 }
修改:{1:3, 3:6, 2:2 }
过量增加:{4:0, 1:3, 3:6 }
获取:3
获取:{1:3, 4:0, 3:6 }
删除:{1:3, 3:6 }

看到hashmap+双向链表?这不就是LinkedHashMap吗?直接上一个简单粗暴的。

LinkedHashMap实现

这种实现方法和第一种有所不同,其将最近访问的放在最后一个节点,而不是第一个节点。

import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache extends LinkedHashMap<Integer, Integer>{
	private int capacity;
    
	/*
	 * 构造函数
	 */
    public LRUCache(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }
    
    /*
     * 获取数据
     */
    public int get(int key) {
        return super.getOrDefault(key, -1);
    }
    
    /*
     * 修改或增加数据
     */
    public void put(int key, int value) {
        super.put(key, value);
    }
    
    /*
     * 删除数据
     */
    public void remove(int key) {
    	super.remove(key);
    }
    
    /*
     * 返回true表示删除最后一个节点
     * 其调用在putVal()方法中的afterNodeInsertion()方法
     */
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return size() > capacity; 
    }
    
    public static void main(String[] args) {  // 测试
		LRUCache lru = new LRUCache(3);
		lru.put(1, 5);  // 增加
		lru.put(2, 2);
		lru.put(3, 6);  
		System.out.println("增加:" + lru.toString());
		lru.put(1, 3);  // 修改
		System.out.println("修改:" + lru.toString());
		lru.put(4, 0);  // 过量增加
		System.out.println("过量增加:" + lru.toString());
		System.out.println("获取:" + lru.get(1));
		System.out.println("获取:" + lru.toString());
		lru.remove(4);  // 删除
		System.out.println("删除:" + lru.toString());
	}
}

运行结果

增加:{1=5, 2=2, 3=6}
修改:{2=2, 3=6, 1=3}
过量增加:{3=6, 1=3, 4=0}
获取:3
获取:{3=6, 4=0, 1=3}
删除:{3=6, 1=3}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值