原理
1、需要给缓存设置固定大小
2、每次读取缓存都该变缓存的使用时间,
3、在缓存满了以后,需要将最早的缓存数据删除,再添加新的元素
LinkedHashMap实现
这是LinkedHashMap的一个构造函数,传入的第三个参数accessOrder为true的时候,就按访问顺序对LinkedHashMap排序,为false的时候就按插入顺序,默认是为false的。 当把accessOrder设置为true后,就可以将最近访问的元素置于最前面,这样就可以满足上述的第二点。
public class LinkedHashMapLRU<K, V> {
private final int MAX_CACHE_SIZE;
private final float DEFAULT_LOAD_FACTORY = 0.75f;
/**
* LRU缓存
*/
private LinkedHashMap<K, V> map;
public LinkedHashMapLRU(int cacheSize) {
this.MAX_CACHE_SIZE = cacheSize;
int capacity = (int) Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1;
map = new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_CACHE_SIZE;
}
};
}
public synchronized void put(K key, V value) {
map.put(key, value);
}
public synchronized V get(K key) {
return map.get(key);
}
public synchronized void remove(K key) {
map.remove(key);
}
public synchronized Set<Map.Entry<K, V>> getAll() {
return map.entrySet();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<K, V> entry : map.entrySet()) {
stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue()));
}
return stringBuilder.toString();
}
public static void main(String[] args) {
LinkedHashMapLRU<Integer, Integer> lru1 = new LinkedHashMapLRU<>(5);
lru1.put(1, 1);
lru1.put(2, 2);
lru1.put(3, 3);
System.out.println(lru1);
lru1.get(1);
System.out.println(lru1);
lru1.put(4, 4);
lru1.put(5, 5);
lru1.put(6, 6);
System.out.println(lru1);
}
}
运行结果
1: 1 2: 2 3: 3
2: 2 3: 3 1: 1
3: 3 1: 1 4: 4 5: 5 6: 6
用HashMap实现
当数据每一次查询就将数据放到链表的head,当有新数据添加时也放到head上。这样链表的tail就是最久没用使用的缓存数据,每次容量不足的时候就可以删除tail,并将前一个元素设置为tail,显然这是一个双向链表结构,因此我们定义LRUNode如下:
LRUNode.java
class LRUNode<K,V> {
K key;
V value;
LRUNode prev;
LRUNode next;
public LRUNode(K key, V value) {
this.key = key;
this.value = value;
}
}
LRUCache.java
public class LRUCache<K, V> {
/**
* 缓存
*/
private HashMap<K, LRUNode<K, V>> map;
/**
* 容量
*/
private int capacity;
/**
* 头结点
*/
private LRUNode head;
/**
* 尾结点
*/
private LRUNode tail;
/**
* 设置缓存
*
* @param key 键
* @param value 值
*/
public void set(K key, V value) {
LRUNode node = map.get(key);
// 节点不为空,则更新值,删删除节点
if (node != null) {
node.value = value;
remove(node, false);
}
// 节点为空,则创建节点
else {
node = new LRUNode(key, value);
if (map.size() >= capacity) {
// 每次容量不足时先删除最久未使用的元素
remove(tail, true);
}
map.put(key, node);
}
// 将刚添加的元素设置为head
setHead(node);
}
/**
* 获取指定的键的值
*
* @param key 键
* @return
*/
public V get(K key) {
LRUNode<K, V> node = map.get(key);
if (node != null) {
// 将刚操作的元素放到head
remove(node, false);
setHead(node);
return node.value;
}
return null;
}
/**
* 将元素添加到head
*
* @param node
*/
private void setHead(LRUNode node) {
// 先从链表中删除该元素
if (head != null) {
node.next = head;
head.prev = node;
}
head = node;
if (tail == null) {
tail = node;
}
}
/**
* 从链表中删除此Node,此时要注意该Node是head或者是tail的情形
*
* @param node
* @param flag
*/
private void remove(LRUNode node, boolean flag) {
if (node.prev != null) {
node.prev.next = node.next;
} else {
head = node.next;
}
if (node.next != null) {
node.next.prev = node.prev;
} else {
tail = node.prev;
}
node.next = null;
node.prev = null;
if (flag) {
map.remove(node.key);
}
}
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new HashMap<K, LRUNode<K, V>>();
}
}