面试时被问到LRU的实现,决定尝试使用Java实现一下
一、基于LinkedHashMap LinkedHashMap中使用了hashmap和双链表,只要重写removeEldestEntry方法,就会在超过固定大小时移除最早的节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class LRU { private LinkedHashMap<Integer,Integer> cache; private final int CAPACITY=10 ; public LRU (int capacity) { cache=new LinkedHashMap<Integer, Integer>(capacity,0.75 f,true ){ @Override protected boolean removeEldestEntry (Map.Entry eldest) { return size()>CAPACITY; } }; } public int get (int key) { return cache.getOrDefault (key,-1 ) ; } public void put (int key,int value) { cache.put(key,value); } }
其中getOrDefault
方法
1 2 3 4 5 6 7 8 public V getOrDefault (Object key , V defaultValue) { Node<K,V> e; if ((e = getNode (hash (key ), key )) == null ) return defaultValue; if (accessOrder) afterNodeAccess (e); return e.value; }
跟踪到afterNodeAccess
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null ; if (b == null ) head = a; else b.after = a; if (a != null ) a.before = b; else last = b; if (last == null ) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
通过源码可以得知当accessOrder为true时,则会在访问了节点以后把当前被访问的节点放到链表的末尾
二、HashMap+双链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public class LRUCachel { class Node { int key; int value; Node pre ; Node next ; public Node (int key, int value){ this.key = key; this.value = value; } } private int capacity; HashMap<Integer,Node> map= new HashMap<> (); Node head ; Node end ; //初始化大小,缓存是有大小限制的,超过规定的大小时就得移除 public LRUCachel(int capacity) { this.capacity = capacity; } //获取一个缓存数据之后,应该把这个数据在当前位置中移除,并重新添加到头的位置,这些都是在返回数据之前完成的 public int get(int key){ if(map.containsKey(key)){ Node node =map.get(key); remove(node ); setHead (node ); return node .value ; } return -1 ; } //移除元素分为,N的前边和N的后边都要看是怎么样的情况 public void remove(Node node ){ if(node .pre !=null){ node .pre .next= node .next ; }else { head= node .next ; } if(node .next !=null){ node .next .pre= node .pre ; }else { end= node .pre ; } } //放刚刚访问到的放到头部 public void setHead(Node node ){ node .next =head; node .pre =null; if(head!=null){ head.pre= node ; } head =node ; if (end= =null){ end= head; } } //设置看原位置是否有元素,如果有的话就替换,这证明使用过了,然后将其替换为头结点的元素,若果是一个新的节点就要判断它的大小是否符合规范 public void set(int key,int value){ if(map.containsKey(key)){ Node old =map.get(key); old.value= value; remove(old); setHead(old); }else { Node created =new Node (key ,value); if (map.size() > capacity) { map.remove(end.key); remove(end); setHead(created); }else { setHead(created); } map.put(key,created); } } }