#  LRU 缓存146.LRU 缓存 
请你设计并实现一个满足 LRU (最近最少使用) 缓存  约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数  作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出  最久未使用的关键字。函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
一开始想到是使用双向链表 ,但是双向链表的查询是 O (n) 的,O (1) 的查询自然就是哈希表 了
PUT操作 :
添加元素时判断 key 是否存在(HashMap 查找)
存在
重新赋值 将节点从原有位置抽离 节点放到链表头部 不存在
判断容量已经满了 ?(用 count 变量记录 cache 中元素数量)
没满:新建一个节点 newNode 插入链表头部
满了:
将尾部节点删除 新建节点 newNode 插入头部 HashMap 存放 < key, newNode >
GET操作 :
从 HashMap 取节点为 null 返回 - 1 不为 null将节点从原有位置抽离 节点放到链表头部   AC 之后考虑并发问题,LRU 本身就是操作系统的内存淘汰机制,需要考虑并发问题:如果 get 的同时
在多线程的情况下, get() 移动节点操作和 put() 整个方法加锁来保证节点的顺序正常。
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 import  java.util.HashMap;import  java.util.HashMap;import  java.util.concurrent.locks.LockSupport;class  LRUCache  {         private  final  int  capacity;          private  final  Node head;          private  final  Node tail;     private  final  HashMap<Integer, Node> hash;     class  Node  {         Node next, prev;         int  key, val;         public  Node ()  {         }         public  Node (int  key, int  val, Node prev, Node next)  {             this .next = next;             this .prev = prev;             this .key = key;             this .val = val;         }         public  Node (int  key, int  val)  {             this .key = key;             this .val = val;         }     }     public  LRUCache (int  capacity)  {         this .capacity = capacity;         this .hash = new  HashMap <>();         head = new  Node ();         tail = new  Node ();         head.next = tail;         tail.prev = head;     }     public  int  get (int  key)  {                  Node  nNode  =  hash.get(key);         if  (nNode != null ) {             unLinkedNode(nNode);             moveToHead(nNode);             return  nNode.val;         }         return  -1 ;     }     public  synchronized  void  put (int  key, int  value)  {         Node  nNode  =  hash.get(key);         if  (nNode == null ) {             if  (hash.size() == capacity) {                 hash.remove(tail.prev.key);                 removeTail();             }             nNode = new  Node (key, value);             hash.put(key, nNode);         } else  {             nNode.val = value;             unLinkedNode(nNode);         }         moveToHead(nNode);     }          private  void  moveToHead (Node cur)  {         head.next.prev = cur;         cur.next = head.next;         head.next = cur;         cur.prev = head;     }         private  void  unLinkedNode (Node cur)  {         cur.prev.next = cur.next;         cur.next.prev = cur.prev;     }          private  void  removeTail ()  {         unLinkedNode(tail.prev);     } } 
#  引申LRU 全程 Least Recently Used ,即最近最少使用的页面置换算法,是服务虚拟页式存储管理服务的,实现思想是读取的数据会更新到 LRU 列表最前端, 当容量满的时候会淘汰尾部数据,来达到一个最近最少使用的数据淘汰功能。
根据局部性原理 (包括空间局部性和时间局部性,一条指令执行后在将来的一段时间内可能会再次执行(热点代码),一个数据被访问后可能会再次访问,或者这个数据相邻的数据很大概率会再次访问到)。
CSAPP 关于局部性的论述:
在一个具有良好时间局部性的程序中,被引用过的内存位置很可能在不远的将来再被多次引用。 在一个具有良好空间局部性的程序中,一个内存位置被引用了一次,那么程序很可能在不远的将来引用附近的一个内存位置。 个人思考:局部性原理是无处不在的,LRU 就是基于局部性原理的一个体现。