LRU,算法在操作缓存中常常被用到,由于其访问频繁,因此缩小LRU时间复杂度是非常必要的,好的LRU算法的实现能够很好的提高系统的稳定性
数据结构中map的访问速度非常快,时间复杂度为O(1),因此在缓存结构中,可以借助map结构,同时由于缓存需要容量满时需要删除操作,并且对于最近被访问的需要重新置于头部,在数据结构中链表能够很好的完成该操作,故缓存结构借助map加链表结构来降低时间复杂度,使得查询、删除、交换的时间复杂度都为O(1),
具体设计如下,map中存放key -value对,查找时可以通过key快速定位到value,
value存放的是node节点,所有的value,都是双向链表中一个node,删除时直接移除
尾部节点即可,为简化操作,链表自带头节点head和尾节点tail
package Inter.other; /** * 缓存通用实现接口接口 * Created by lin on 2018/9/19. */ public interface Cache<K, V> { <V> V get(K key); void set(K key, V value); void clear(); }
package Inter.other; /** * 键值生成策略接口 * Created by lin on 2018/9/19. */ public interface KeyGenerationStrategy<K, V> { K generationKey(V value); }
package Inter.other; /** * 链表节点的定义 * Created by lin on 2018/9/16. */ public class Node<K,V> { V value; K key;//表示该节点的键; Node next; Node prev; public Node(V value, K key) { this.value = value; this.key = key; } public Node(Node prev, Node next, V value) { this.prev = prev; this.next = next; this.value = value; } public K getKey() { return this.key; } @Override public String toString() { return "prev:" + prev.value + "当前节点" + this.value + "next:" + next.value; } }
package Inter.other; /** * 简单的键值生成 * Created by lin on 2018/9/19. */ public class SimpleKeyGenerationStrategy<K, V> implements KeyGenerationStrategy<K, V> { @Override public K generationKey(V value) { return (K) value.toString(); } }
package Inter.other; import java.util.HashMap; import java.util.Map; /** * 缓存算法的具体实现 * Created by lin on 2018/9/16. * 时间复杂度为O(1)的一个缓存 */ public class LRUCache<K, V> implements Cache<K, V> { // private KeyGenerationStrategy<K, V> keyGenerationStrategy; //默认容量大小 private static final int DEFAULT_CAPACITY = 8; /* 缓存容量的大小 */ private int capacity; /* 缓存已使用的容量 */ private int size; /* 为了实现快速寻找,这里使用map,查找时间复杂度为O(1)*/ private Map<K, Node<K, V>> map = new HashMap<>(); /* 为了实现快速替换,这里使用链表,删除或者加入时间复杂度为O(1)*/ private Node<K, V> head; private Node<K, V> tail; /** * 初始化 * * @param capacity */ public LRUCache(int capacity) { // map = new HashMap<>(); if (capacity <= 0) { capacity = DEFAULT_CAPACITY; } this.capacity = capacity; this.head = new Node<K, V>(null, null, null); this.tail = new Node<K, V>(head, null, null); head.next = tail; } /** * 从缓存中获取指定值,没有返回空 * * @param * @param <V> * @return */ @Override public <V> V get(K key) { Node<K, V> node = (Node<K, V>) map.get(key); if (node == null) { return null; } else { moveToFirst(node); return node.value; } } /** * 指定节点添加到缓存中 * * @param key value值对应的键 * @param value 存放的值 */ @Override public void set(K key, V value) { Node<K, V> node = new Node(value, key); //缓存容量未满,不需要淘汰,直接添加到最后一个 if (size <= capacity) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; map.put(node.key, node); size++; } else {//容量已满,淘汰最后一个节点即可 // map.put((K)node.key, node); Node delNode = tail.prev; delNode.prev.next = node; node.prev = delNode.prev; node.next = tail; tail.prev = node; delNode.next = null; delNode.prev = null; delNode = null; map.remove(delNode.key); } } //清空缓存 @Override public void clear() { this.head = new Node<K, V>(null, null, null); this.tail = new Node<K, V>(head, null, null); head.next = tail; size = 0; } /** * 当节点被访问时需要放置到缓存最前面 * * @param node */ private void moveToFirst(Node node) { //validationIsSwap(); if (node == head.next) { return; } Node<K, V> nodePrev = node.prev; Node<K, V> nodeNext = node.next; Node beMoved = head.next;// 头节点的下一个节点 head.next = node; node.prev = head; node.next = beMoved; beMoved.prev = node; nodePrev.next = nodeNext; nodeNext.prev = nodePrev; } /** * 确定是否可以交换,如果size小于等于1 则没必要 * <p> * private void validationIsSwap() { * if (size <= 1) { * throw new IllegalArgumentException("缓存容量不大于1,不能进行该操作"); * } * } */ public static void main(String[] args) { LRUCache<String, Integer> lruCache = new LRUCache(20); KeyGenerationStrategy<String, Integer> keyGenerationStrategy = new SimpleKeyGenerationStrategy<>(); String key1 = keyGenerationStrategy.generationKey(1); String key2 = keyGenerationStrategy.generationKey(2); String key3 = keyGenerationStrategy.generationKey(3); lruCache.set(key1, 1); lruCache.set(key2, 2); lruCache.set(key3, 3); System.out.println(lruCache.get(key1)+""); ; System.out.println(lruCache.get(key2)+""); ; System.out.println(lruCache.get(key3)+""); ; System.out.println(lruCache.get(key1)+""); ; // lruCache.swapAndFirst(node2); Node head = lruCache.head; //第一个 head = head.next; System.out.println(head); //第二个 head = head.next; System.out.println(head); //第三个 head = head.next; System.out.println(head); // lruCache.set(node1); } private Node getHead() { return this.head; } }