缓存算法方式
面试经历来看,最常听到的答案就是:数组查找的时间复杂度是O(1),但是删除、插入数据的时间复杂度是O(n);而链表查找的时间复杂度是O(n),但是删除、插入数据的时间复杂度是O(1)。
1. LRU (Least recently used):最近最少使用,如果数据最近被访问过,那么将来被访问的几率也更高。
2. LFU (Least frequently used):最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。
3. FIFO (Fist in first out):先进先出。如果一个数据最先进入缓存中,则应该最早淘汰掉。
原理:保证get的值位于数组最前面,表示该值最近经常使用;如果数组元素个数达到size,则优先将最不经常使用的值删除掉,也就是数组中最后的值。
方式一:LinkedHashMap实现方式
```java
public class LruCache<K,V> extends LinkedHashMap<K,V> {
private int capacity;
public LruCache(int capacity){
//accessOrder 表示访问排序,每次访问之后会优先将值置于链表最前面
super(16,0.75f,true);
this.capacity = capacity;
}
// 返回true,会优先删除链表中旧值
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size()>capacity;
}
public static void main(String[] args) {
LruCache<String,String> cache = new LruCache<String, String>(3);
cache.put("a","a");
cache.put("b","b");
cache.put("c","c");
cache.forEach((k,v)-> System.out.println("k = " + k));
System.out.println("-------------");
cache.get("b");
cache.forEach((k,v)-> System.out.println("k = " + k));
System.out.println("**************");
cache.put("d","d");
cache.forEach((k,v)-> System.out.println("k = " + k));
}
}
```
方式二、双向链表实现
hashMap理想状态下查找和删除的时间复杂度均为O(1)。O(n)是最坏情况下,也就是所有key都存在hash冲突,导致遍历链表。缺点是无法记录最近最少使用。
链表:查找的时间复杂度为O(n)。删除和插入的时间复杂度为O(1)。
所以在理想情况下,设计LRU缓存模型时,将维护节点间使用关系的链表设计成双向,避免单向链表增加时间复杂度。
思路:双向链表记录数据使用、添加后的前后关系。
hashMap是作为查找缓存数据的容器。
```java
public class LruCache {
private int capacity;
private int size;
private LruNode head;
private LruNode tail;
HashMap<String,LruNode> hashMap = new HashMap<>();
public LruCache(int capacity){
this.capacity = capacity;
this.head = new LruNode();
this.tail = new LruNode();
head.next = tail;
tail.pre = head;
}
public int get(String key){
LruNode lruNode = hashMap.get(key);
if(lruNode != null){
//将其放到头结点
moveToHead(lruNode);
return lruNode.value;
}else {
return -1;
}
}
public LruNode put(String key,int value){
LruNode lruNode = hashMap.get(key);
if(lruNode == null){
lruNode = new LruNode(key,value);
hashMap.put(key,lruNode);
addToHead(lruNode);
size ++;
if(size > capacity){
LruNode tail = removeTailNode();
hashMap.remove(tail.key);
size --;
}
}else {
lruNode.value = value;
moveToHead(lruNode);
}
return lruNode;
}
/**
* 删除尾部的节点
* @return
*/
private LruNode removeTailNode() {
LruNode node = tail.pre;
removeNode(node);
return node;
}
/**
* 头插法
* @param newNode
*/
private void addToHead(LruNode newNode) {
newNode.next = head.next;
newNode.pre = head;
head.next.pre = newNode;
head.next = newNode;
}
/**
* 删除当前节点
* @param node
*/
private void removeNode(LruNode node){
node.pre.next = node.next;
node.next.pre = node.pre;
}
/**
* 1、删除当前节点
* 2、添加到头结点
* @param lruNode
*/
private void moveToHead(LruNode lruNode) {
removeNode(lruNode);
addToHead(lruNode);
}
}
class LruNode{
public String key;
public int value;
public LruNode pre;
public LruNode next;
public LruNode(){
};
public LruNode(String key,int value){
this.key = key;
this.value = value;
}
}
```