LRU Cache缓存机制


不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!

LRU Cache缓存机制

概念

LRU Cache缓存机制即利用LRU算法实现的缓存机制。

LRU算法

概念

是一种缓存淘汰策略。LRU全称Least Recently Used,即最近最久未使用。表示这一块缓存中的数据,已经很长时间没有被使用过了。

原理
  • get方法
  1. 如果加载的是缓存区中存在的数据,那么使用该数据,并将该数据放置在缓存区开头
  2. 如果加载的是缓存区中不存在的数据,并且缓存区中还有内存,那么网络请求数据,并把数据放入缓存区开头
  3. 如果加载的是缓存区中不存在的数据,但是缓存区中已经没有内存可以去利用了,那么就将根据LRU原则,将最近最久未使用的数据删除,回收缓存区,一般都在缓存区的末尾
  • put方法
  1. 数据在缓存中已存在,将那个数据移到开头
  2. 数据不存在缓存中,缓存空间充足放入缓存中。
  3. 数据不存在缓存中,缓存空间不足,根据最近最久未使用原则,删除最后一个数据,然后将数据放入缓存。
  • delete方法

空间不足时使用,根据LRU原则,删除最后一个数据。

实现
数组

使用这种连续存储的空间当做缓存区。其中放置的数据对象有大小访问时间戳。每次访问、插入、更新的时候都更新这个时间戳。如果内存不够,需要删除时,删除时间戳最老的即可。

链表

使用这种链式存储的空间当做缓存区,比如队列双向链表。将插入、更新、访问的结点放在开头,将最久没使用的放在末尾。所以删除最近最久未使用的数据,只需要删除最后一个结点即可。

实现

  • 一个初始化方法,设置缓存区大小,定义数据结构。
  • 一个get方法,通过传入key,返回value。
  • 一个put方法,通过key,value,将value保存在key的位置。
  • 一个delete方法,删除最近最久未使用的数据,回收缓存区。

使用ArrayList实现一个最简单的LruCache(最容易理解)

思路:构造方法定义容量和数据结构ArrayList,put方法向ArrayList加入数据,get方法请求数据,delete方法删除最先加入的数据。

  • LruCache类
package com.hnucm;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;

public class LruCache {
    //定义缓存容量
	int maxSize = 0;
    //定义数据结构
	ArrayList<Integer> list = null;
	
    //构造函数初始化缓存容量和数据结构
	public LruCache(int size){
		this.maxSize = size;
		list = new ArrayList<Integer>();
	}
	
    //向缓存中加入数据
	public void put(int val) {
		//缓存中已存在
		if(list.contains(val)) {
			//调用数据,将数据移到末尾
			list.remove(val);
		}else {
            //不存在
            //容量不足
			if(list.size() == maxSize) {
                //删除第一个数据
				delete();
			}
		}
        //加入缓存中
		list.add(val);
	}
	
    //请求数据
	public void get(int val) {
        //缓存中不存在
		if(!list.contains(val)) {
			System.out.println("缓存中不存在这个数,需要网络请求这个数据!返回的"
					+ "数据还需判断缓存是否还有空间存储");
		}else {
            //存在,使用数据,并移到末尾
			list.remove(val);
			list.add(val);
		}
	}
	
	//删除最近最久未使用的缓存
	//即第一个数值
	public void delete() {
		list.remove(list.get(0));
	}
}
  • 测试数据类
package com.hnucm;

public class Main1 {
	public static void main(String[] args) {
		LruCache lruCache = new LruCache(2);
		lruCache.put(1);
		lruCache.put(2);
		System.out.println(lruCache.list);
		lruCache.get(3);
		lruCache.put(3);
		System.out.println(lruCache.list);
	}
}

使用LinkedHashMap实现LruCache(最经典实现)

LinkedHashMap是双向链表的数据结构,安卓中LruCache源码中就是使用的这个数据结构。

思路:和ArrayList使用的思路大致一样,唯一的不同在于根据key删除数据,所以找到最先加入的数据,需要使用迭代器。

  • LruCache类
package com.hnucm;

import java.security.KeyStore.Entry;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

public class LruCache<K, V> {
	// 定义缓存容量
	int maxSize = 0;
	// 定义数据结构
	// 双向链表 + HashMap
	LinkedHashMap<K, V> map = null;
	

	// 构造函数初始化
	public LruCache(int size) {
		this.maxSize = size;
		map = new LinkedHashMap<K, V>();
	}
	
	/*
	 * 将数据存入缓存
	 */
	public void put(K key, V value) {
		// 缓存中已存在
		if (map.containsKey(key)) {
			// 调用数据,将数据移到开头
			map.remove(key);
		} else {
			// 缓存中不存在
			// 内存不足
			if (map.size() == maxSize) {
				//根据LRU原则删除第一个数据
				//使用迭代器获取第一个值的key
				map.remove(map.entrySet().iterator().next().getKey());
			}
		}
		// 数据放入缓存
		map.put(key, value);
	}

	/*
	 * 请求数据
	 */
	public void get(K key) {
		//缓存中没有,需要网络请求
		if (!map.containsKey(key)) {
			System.out.println("缓存中不存在这个数,需要网络请求这个数据!返回的" + "数据还需判断缓存是否还有空间存储");
		} else {
			//缓存中存在
			//map的get会使用它并把它放在末尾
			V value = map.get(key);
			map.remove(key);
			map.put(key,value);
		}
	}
}
  • 测试数据类
package com.hnucm;

public class Main1 {
	public static void main(String[] args) {
		LruCache<Integer,Integer> lruCache = new LruCache<>(2);
		lruCache.put(1,1);
		lruCache.put(2,2);
		System.out.println(lruCache.map);
		lruCache.get(1);
		System.out.println(lruCache.map);
		lruCache.get(3);
		lruCache.put(3,3);
		System.out.println(lruCache.map);
		lruCache.put(3,4);
		System.out.println(lruCache.map);
	}
}

使用队列实现LrcCache(类似于ArrayList)

  • LruCache类
package com.hnucm;

import java.util.LinkedList;
import java.util.Queue;

public class LruCache{
	// 定义缓存容量
	int maxSize = 0;
	// 定义数据结构
	// 队列
	Queue<Integer> queue = null;
	

	// 构造函数初始化
	public LruCache(int size) {
		this.maxSize = size;
		queue = new LinkedList<Integer>();
	}
	
	/*
	 * 将数据存入缓存
	 */
	public void put(int value) {
		// 缓存中已存在
		if (queue.contains(value)) {
			// 调用数据,将数据移到末尾
			queue.remove(value);
		} else {
			// 缓存中不存在
			// 内存不足
			if (queue.size() == maxSize) {
				//根据LRU原则删除第一个数据
				queue.poll();
			}
		}
		// 数据放入缓存
		queue.offer(value);
	}

	/*
	 * 请求数据
	 */
	public void get(int value) {
		//缓存中没有,需要网络请求
		if (!queue.contains(value)) {
			System.out.println("缓存中不存在这个数,需要网络请求这个数据!返回的" + "数据还需判断缓存是否还有空间存储");
		} else {
			//缓存中存在
			//使用并放入末尾
			queue.remove(value);
			queue.offer(value);
		}
	}
}

  • 测试数据类
package com.hnucm;

public class Main1 {
	public static void main(String[] args) {
		LruCache lruCache = new LruCache(2);
		lruCache.put(1);
		lruCache.put(2);
		System.out.println(lruCache.queue);
		lruCache.get(1);
		System.out.println(lruCache.queue);
		lruCache.get(3);
		lruCache.put(3);
		System.out.println(lruCache.queue);
		lruCache.put(4);
		System.out.println(lruCache.queue);
	}
}

总结

所有的实现几乎都差不多,思路都是最近最久未使用,只是采用的数据结构不同。put就是有空间就直接放入,没空间就去掉最久未使用的缓存,回收空间去放置。get就是更新数据的使用,将越久没使用的,放在开头,最新使用的在末尾。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值