1. //基于 hash (拉链法) + 双向链表,LRUcache
  2. //若改为开放寻址,线性探测法能更好使用cpuCache
  3. public class LRU {
  4. private class Node {
  5. Node p; //访问序 priv
  6. Node n; //访问序 next
  7.  
  8. Node hn; //hash 拉链 next
  9. Object key;
  10. Object val;
  11.  
  12. public Node() {
  13. }
  14.  
  15. public Node(Object key, Object val) {
  16. this.key = key;
  17. this.val = val;
  18. }
  19.  
  20. @Override
  21. public String toString() {
  22. return "Node{" +
  23. "key=" + key +
  24. ", val=" + val +
  25. ", hn=" + (hn == null ? "n" : hn.key) +
  26. ", p=" + (p == null ? "n" : p.key) +
  27. ", n=" + (n == null ? "n" : n.key) +
  28. '}';
  29. }
  30. }
  31.  
  32. Node head;
  33. Node tail;
  34. Node[] tables;
  35. int capa = 4;
  36. int tabSize; //2的整数倍
  37. int count;
  38. int countLimit = 8;
  39. float loadFactor = 0.75f; //装载因子
  40.  
  41. public LRU(int countLimit) {
  42. this.countLimit = countLimit;
  43. tabSize = capa;
  44. tables = new Node[tabSize];
  45. count = 0;
  46. }
  47.  
  48. public LRU() {
  49. tabSize = capa;
  50. tables = new Node[tabSize];
  51. count = 0;
  52. }
  53.  
  54. public int getTabSize() {
  55. return tabSize;
  56. }
  57.  
  58. public int getCountLimit() {
  59. return countLimit;
  60. }
  61.  
  62. public int getCount() {
  63. return count;
  64. }
  65.  
  66. public void put(Object key, Object val) {
  67. int indexh = hash(key);
  68. Node newNode = null;
  69.  
  70. resize();
  71.  
  72. //插入hash表
  73. if (tables[indexh] == null) {
  74. newNode = new Node(key, val);
  75. tables[indexh] = newNode;
  76. count++;
  77. } else {
  78. Node sentry = new Node();
  79. sentry.hn = tables[indexh];
  80. while (sentry.hn != null) { //hash相同,在同一个桶里,需要额外判断equals
  81. if (sentry.hn.key.equals(key)) {
  82. sentry.hn.val = val; //相同的key,替换val
  83. newNode = sentry.hn;
  84. break;
  85. } else
  86. sentry = sentry.hn; //key不相同继续找拉链的下一个
  87. }
  88. if (newNode == null) { //没有存在有相同key的节点,创建一个新的插入
  89. newNode = new Node(key, val);
  90. sentry.hn = newNode; //拉链尾接上新节点
  91. count++;
  92. }
  93. }
  94.  
  95. //修改访问序链表
  96. if (head == null)
  97. head = newNode;
  98. if (tail == null) {
  99. tail = newNode;
  100. } else {
  101. if (newNode.p != null) //已存在的中间节点,从链表中取出
  102. newNode.p.n = newNode.n;
  103. if (newNode.n != null)
  104. newNode.n.p = newNode.p;
  105. newNode.n = null;
  106. newNode.p = tail; //放到链表尾部
  107. tail.n = newNode;
  108. tail = tail.n;
  109. }
  110.  
  111. if (count > countLimit) {
  112. System.out.println("count > countLimit , del :" + del(head.key));
  113. }
  114. }
  115.  
  116. public Node get(Object key) {
  117. int indexh = hash(key);
  118.  
  119. //从hash表中查找
  120. Node chainHead = tables[indexh];
  121. while (chainHead != null) { //hash相同,在同一个桶里,需要额外判断equals
  122. if (!chainHead.key.equals(key)) //key不相同继续找拉链的下一个
  123. chainHead = chainHead.hn;
  124. break; //找到了
  125. }
  126.  
  127. //处理访问序链表,将访问的节点放到最后
  128. if (chainHead != null && tail != chainHead) {
  129. if (chainHead.p != null)
  130. chainHead.p.n = chainHead.n;
  131. if (chainHead.n != null)
  132. chainHead.n.p = chainHead.p;
  133. if (head == chainHead) {
  134. head = head.n;
  135. }
  136. tail.n = chainHead;
  137. chainHead.p = tail;
  138. chainHead.n = null;
  139. tail = tail.n;
  140. }
  141.  
  142. return chainHead;
  143. }
  144.  
  145. public static class Pair {
  146. public Object key;
  147. public Object val;
  148.  
  149. @Override
  150. public String toString() {
  151. return "{" +
  152. "key=" + key +
  153. ", val=" + val +
  154. '}';
  155. }
  156. }
  157.  
  158. public List<Pair> getAll() {
  159. List<Pair> list = new ArrayList<>();
  160. for (Node cur = head; cur != null; cur = cur.n) {
  161. Pair p = new Pair();
  162. p.key = cur.key;
  163. p.val = cur.val;
  164. list.add(p);
  165. }
  166. return list;
  167. }
  168.  
  169. public Node del(Object key) {
  170. int indexh = hash(key);
  171. Node chainHead = tables[indexh];
  172. Node delNode = null;
  173. Node delHnodeP = null;
  174.  
  175. //从hash表中移除
  176. while (chainHead != null) { //hash相同,在同一个桶里,需要额外判断equals
  177. if (!chainHead.key.equals(key)) //key不相同继续找拉链的下一个
  178. chainHead = chainHead.hn;
  179. else {
  180. delNode = chainHead; //找到目标节点
  181.  
  182. if (delHnodeP != null) { //中间节点
  183. delHnodeP.hn = delNode.hn;
  184. } else { //tables 头节点
  185. tables[indexh] = delNode.hn;
  186. }
  187. break;
  188. }
  189. delHnodeP = chainHead;
  190. }
  191.  
  192. //从访问序链表中移除
  193. if (delNode != null) {
  194. if (delNode.p != null) //从链表中取出
  195. delNode.p.n = delNode.n;
  196. if (delNode.n != null) {
  197. delNode.n.p = delNode.p;
  198. }
  199. if (tail == delNode) //链表头尾处理
  200. tail = delNode.p;
  201. if (head == delNode)
  202. head = delNode.n;
  203. count--;
  204. }
  205.  
  206. return delNode;
  207. }
  208.  
  209. public int hash(Object key) {
  210. int hashc = key.hashCode();
  211. hashc = hashc ^ (hashc >> 16);
  212. int indexh = hashc & (tabSize - 1);
  213. return indexh;
  214. }
  215.  
  216. //扩容
  217. public void resize() {
  218. if (loadFactor * capa > count)
  219. return;
  220.  
  221. System.out.println("resize " + capa + " to " + (capa << 1));
  222. List<Pair> list = getAll();
  223. capa = capa << 1;
  224. tabSize = capa;
  225. Node[] newTables = new Node[tabSize];
  226.  
  227. head = null;
  228. tail = null;
  229. count = 0;
  230. tables = newTables;
  231. for (Pair p : list) {
  232. put(p.key, p.val);
  233. }
  234. }
  235.  
  236. public static void main(String[] args) {
  237. //测试
  238. //add
  239. LRU lru = new LRU();
  240. lru.put(1, 1);
  241. lru.put(2, 2);
  242. lru.put(5, 5);
  243. lru.put(7, 7);
  244.  
  245. System.out.println(lru.getCount());
  246. for (Pair p : lru.getAll()) {
  247. System.out.println(p);
  248. }
  249. System.out.println();
  250.  
  251. //get
  252. System.out.println(lru.get(2));
  253. System.out.println(lru.getCount());
  254. for (Pair p : lru.getAll()) {
  255. System.out.println(p);
  256. }
  257. System.out.println();
  258.  
  259. //del
  260. System.out.println(lru.del(5));
  261. System.out.println(lru.getCount());
  262. for (Pair p : lru.getAll()) {
  263. System.out.println(p);
  264. }
  265. System.out.println();
  266.  
  267. //same key
  268. lru.put(7, 72);
  269. for (Pair p : lru.getAll()) {
  270. System.out.println(p);
  271. }
  272. System.out.println();
  273.  
  274. //hash collision
  275. lru.put(7 + lru.getTabSize(), 73);
  276. System.out.println(lru.getCount());
  277. for (Pair p : lru.getAll()) {
  278. System.out.println(p);
  279. }
  280. System.out.println();
  281.  
  282. //get bucket chain head
  283. lru.get(7);
  284. for (Pair p : lru.getAll()) {
  285. System.out.println(p);
  286. }
  287. System.out.println();
  288.  
  289. //del
  290. lru.del(23);
  291. System.out.println(lru.getCount());
  292. for (Pair p : lru.getAll()) {
  293. System.out.println(p);
  294. }
  295. System.out.println();
  296.  
  297. lru.put(8, 8);
  298. lru.put(9, 9);
  299. lru.put(10, 10);
  300. lru.put(11, 11);
  301. lru.put(12, 12);
  302. lru.put(13, 13); //del 1
  303. lru.put(14, 14); //del 2
  304. System.out.println(lru.getCount());
  305. for (Pair p : lru.getAll()) { //7~14
  306. System.out.println(p);
  307. }
  308. System.out.println();
  309. }
  310. }

输出

  1. resize 4 to 8
  2. 4
  3. {key=1, val=1}
  4. {key=2, val=2}
  5. {key=5, val=5}
  6. {key=7, val=7}
  7.  
  8. Node{key=2, val=2, hn=n, p=7, n=n}
  9. 4
  10. {key=1, val=1}
  11. {key=5, val=5}
  12. {key=7, val=7}
  13. {key=2, val=2}
  14.  
  15. Node{key=5, val=5, hn=n, p=1, n=7}
  16. 3
  17. {key=1, val=1}
  18. {key=7, val=7}
  19. {key=2, val=2}
  20.  
  21. {key=1, val=1}
  22. {key=7, val=7}
  23. {key=2, val=2}
  24. {key=7, val=72}
  25.  
  26. 5
  27. {key=1, val=1}
  28. {key=7, val=7}
  29. {key=2, val=2}
  30. {key=7, val=72}
  31. {key=15, val=73}
  32.  
  33. {key=1, val=1}
  34. {key=7, val=7}
  35. {key=2, val=2}
  36. {key=15, val=73}
  37. {key=7, val=72}
  38.  
  39. 5
  40. {key=1, val=1}
  41. {key=7, val=7}
  42. {key=2, val=2}
  43. {key=15, val=73}
  44. {key=7, val=72}
  45.  
  46. resize 8 to 16
  47. count > countLimit , del :Node{key=1, val=1, hn=9, p=n, n=2}
  48. count > countLimit , del :Node{key=2, val=2, hn=n, p=n, n=15}
  49. count > countLimit , del :Node{key=15, val=73, hn=n, p=n, n=7}
  50. 8
  51. {key=7, val=72}
  52. {key=8, val=8}
  53. {key=9, val=9}
  54. {key=10, val=10}
  55. {key=11, val=11}
  56. {key=12, val=12}
  57. {key=13, val=13}
  58. {key=14, val=14}

LRU hashMap(拉链) + 双向链表 java实现的更多相关文章

  1. How HashMap works in java 2

    https://www.javacodegeeks.com/2014/03/how-hashmap-works-in-java.html   Most common interview questio ...

  2. 昨天面试被问到的 缓存淘汰算法FIFO、LRU、LFU及Java实现

    缓存淘汰算法 在高并发.高性能的质量要求不断提高时,我们首先会想到的就是利用缓存予以应对. 第一次请求时把计算好的结果存放在缓存中,下次遇到同样的请求时,把之前保存在缓存中的数据直接拿来使用. 但是, ...

  3. LRU的理解与Java实现

    简介 LRU(Least Recently Used)直译为"最近最少使用".其实很多老外发明的词直译过来对于我们来说并不是特别好理解,甚至有些词并不在国人的思维模式之内,比如快速 ...

  4. 线性链表的双向链表——java实现

    .线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素. 链表又可分为: 单链表:每个节点只保留一个引用,该引用指向当前节点的下一个节点,没有引用指向头结点,尾节点的next引用为 ...

  5. HashMap如何工作 - Java

    大多数人应该会同意HashMap是现在面试最喜欢问的主题之一.我和同事常常进行讨论,并很有帮助.现在,我继续和大家讨论. 我假设你对HashMap的内部工作原理感兴趣,并且你已经知道了基本的HashM ...

  6. 双向链表--Java实现

    /*双向链表特点: *1.每个节点含有两个引用,previos和next,支持向前或向后的遍历(除头节点) *2.缺点插入或删除的时候涉及到引用修改的比较多 *注意:下面的双向链表其实也实现了双端链表 ...

  7. SpringMvc中Hashmap操作遇到 java.util.ConcurrentModificationException: null

    代码按照网上修改为类似,还不能解决问题 for (Iterator<String> it = target.keySet().iterator(); it.hasNext(); ) { i ...

  8. 双向链表-java完全解析

    原文:https://blog.csdn.net/nzfxx/article/details/51728516 "双向链表"-数据结构算法-之通俗易懂,完全解析 1.概念的引入 相 ...

  9. How HashMap works in Java

    https://www.javainterviewpoint.com/hashmap-works-internally-java/ How a HashMap Works internally has ...

随机推荐

  1. Sqoop 抽数报错: java.io.FileNotFoundException: File does not exist

    Sqoop 抽数报错: java.io.FileNotFoundException: File does not exist 一.错误详情 2019-10-17 20:04:49,080 INFO [ ...

  2. jquery.i18n 网站呈现各国语言

    在做网站的时候可能会遇到不同语言切换的问题,实现的方法有很多种,本篇文章按照 js 加载的方法的来实现. 应用到的 js 文件: jquery.i18n.properties.js jquery.js ...

  3. weed3-2.1.开始纯java使用

    Weed3 一个微型ORM框架(只有0.1Mb哦) 源码:https://github.com/noear/weed3 源码:https://gitee.com/noear/weed3 纯java使用 ...

  4. Android Studio出现Failed to open zip file问题的解决方法

    直接在网上找到gradle-3.3-all.zip下载下来,不要解压缩,放在类似下面的目录中 C:\Users\Administrator\.gradle\wrapper\dists\gradle-3 ...

  5. java 网站源码 在线编辑模版 代码编辑器 兼容手机平板PC freemaker 静态引擎

    前台: 支持四套模版, 可以在后台切换   系统介绍: 1.网站后台采用主流的 SSM 框架 jsp JSTL,网站后台采用freemaker静态化模版引擎生成html 2.因为是生成的html,所以 ...

  6. LATEX Mathematical Symbols

    原文地址:https://www.caam.rice.edu/~heinken/latex/symbols.pdf

  7. java读取文本文件内容2

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/183 很久之前写了一篇Java读取文本文件内容,链接地址是 ...

  8. Windows和linux下的端口转发

    利用VPN,实现无公网IP或内网服务器的服务 @@@code netsh interface portproxy add v4tov4 listenport=8887 connectaddress=1 ...

  9. Dockerfile优化

    总结: 1.编写.dockerignore文件 2.容器只运行单个应用 3.将多个RUN指令合并为一个 4.基础镜像的标签不要用latest 5.每个RUN指令后删除多余文件 6.选择合适的基础镜像( ...

  10. python-将一个列表切分成多个小列表

    list是python中较为常见的数据类型,它是一个可迭代对象,迭代是什么?简单的可以理解成:一个可以被for循环遍历的对象 今天拿到一个类似这样的list list_info = ['name zh ...