//基于 hash (拉链法) + 双向链表,LRUcache
//若改为开放寻址,线性探测法能更好使用cpuCache
public class LRU {
private class Node {
Node p; //访问序 priv
Node n; //访问序 next Node hn; //hash 拉链 next
Object key;
Object val; public Node() {
} public Node(Object key, Object val) {
this.key = key;
this.val = val;
} @Override
public String toString() {
return "Node{" +
"key=" + key +
", val=" + val +
", hn=" + (hn == null ? "n" : hn.key) +
", p=" + (p == null ? "n" : p.key) +
", n=" + (n == null ? "n" : n.key) +
'}';
}
} Node head;
Node tail;
Node[] tables;
int capa = 4;
int tabSize; //2的整数倍
int count;
int countLimit = 8;
float loadFactor = 0.75f; //装载因子 public LRU(int countLimit) {
this.countLimit = countLimit;
tabSize = capa;
tables = new Node[tabSize];
count = 0;
} public LRU() {
tabSize = capa;
tables = new Node[tabSize];
count = 0;
} public int getTabSize() {
return tabSize;
} public int getCountLimit() {
return countLimit;
} public int getCount() {
return count;
} public void put(Object key, Object val) {
int indexh = hash(key);
Node newNode = null; resize(); //插入hash表
if (tables[indexh] == null) {
newNode = new Node(key, val);
tables[indexh] = newNode;
count++;
} else {
Node sentry = new Node();
sentry.hn = tables[indexh];
while (sentry.hn != null) { //hash相同,在同一个桶里,需要额外判断equals
if (sentry.hn.key.equals(key)) {
sentry.hn.val = val; //相同的key,替换val
newNode = sentry.hn;
break;
} else
sentry = sentry.hn; //key不相同继续找拉链的下一个
}
if (newNode == null) { //没有存在有相同key的节点,创建一个新的插入
newNode = new Node(key, val);
sentry.hn = newNode; //拉链尾接上新节点
count++;
}
} //修改访问序链表
if (head == null)
head = newNode;
if (tail == null) {
tail = newNode;
} else {
if (newNode.p != null) //已存在的中间节点,从链表中取出
newNode.p.n = newNode.n;
if (newNode.n != null)
newNode.n.p = newNode.p;
newNode.n = null;
newNode.p = tail; //放到链表尾部
tail.n = newNode;
tail = tail.n;
} if (count > countLimit) {
System.out.println("count > countLimit , del :" + del(head.key));
}
} public Node get(Object key) {
int indexh = hash(key); //从hash表中查找
Node chainHead = tables[indexh];
while (chainHead != null) { //hash相同,在同一个桶里,需要额外判断equals
if (!chainHead.key.equals(key)) //key不相同继续找拉链的下一个
chainHead = chainHead.hn;
break; //找到了
} //处理访问序链表,将访问的节点放到最后
if (chainHead != null && tail != chainHead) {
if (chainHead.p != null)
chainHead.p.n = chainHead.n;
if (chainHead.n != null)
chainHead.n.p = chainHead.p;
if (head == chainHead) {
head = head.n;
}
tail.n = chainHead;
chainHead.p = tail;
chainHead.n = null;
tail = tail.n;
} return chainHead;
} public static class Pair {
public Object key;
public Object val; @Override
public String toString() {
return "{" +
"key=" + key +
", val=" + val +
'}';
}
} public List<Pair> getAll() {
List<Pair> list = new ArrayList<>();
for (Node cur = head; cur != null; cur = cur.n) {
Pair p = new Pair();
p.key = cur.key;
p.val = cur.val;
list.add(p);
}
return list;
} public Node del(Object key) {
int indexh = hash(key);
Node chainHead = tables[indexh];
Node delNode = null;
Node delHnodeP = null; //从hash表中移除
while (chainHead != null) { //hash相同,在同一个桶里,需要额外判断equals
if (!chainHead.key.equals(key)) //key不相同继续找拉链的下一个
chainHead = chainHead.hn;
else {
delNode = chainHead; //找到目标节点 if (delHnodeP != null) { //中间节点
delHnodeP.hn = delNode.hn;
} else { //tables 头节点
tables[indexh] = delNode.hn;
}
break;
}
delHnodeP = chainHead;
} //从访问序链表中移除
if (delNode != null) {
if (delNode.p != null) //从链表中取出
delNode.p.n = delNode.n;
if (delNode.n != null) {
delNode.n.p = delNode.p;
}
if (tail == delNode) //链表头尾处理
tail = delNode.p;
if (head == delNode)
head = delNode.n;
count--;
} return delNode;
} public int hash(Object key) {
int hashc = key.hashCode();
hashc = hashc ^ (hashc >> 16);
int indexh = hashc & (tabSize - 1);
return indexh;
} //扩容
public void resize() {
if (loadFactor * capa > count)
return; System.out.println("resize " + capa + " to " + (capa << 1));
List<Pair> list = getAll();
capa = capa << 1;
tabSize = capa;
Node[] newTables = new Node[tabSize]; head = null;
tail = null;
count = 0;
tables = newTables;
for (Pair p : list) {
put(p.key, p.val);
}
} public static void main(String[] args) {
//测试
//add
LRU lru = new LRU();
lru.put(1, 1);
lru.put(2, 2);
lru.put(5, 5);
lru.put(7, 7); System.out.println(lru.getCount());
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); //get
System.out.println(lru.get(2));
System.out.println(lru.getCount());
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); //del
System.out.println(lru.del(5));
System.out.println(lru.getCount());
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); //same key
lru.put(7, 72);
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); //hash collision
lru.put(7 + lru.getTabSize(), 73);
System.out.println(lru.getCount());
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); //get bucket chain head
lru.get(7);
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); //del
lru.del(23);
System.out.println(lru.getCount());
for (Pair p : lru.getAll()) {
System.out.println(p);
}
System.out.println(); lru.put(8, 8);
lru.put(9, 9);
lru.put(10, 10);
lru.put(11, 11);
lru.put(12, 12);
lru.put(13, 13); //del 1
lru.put(14, 14); //del 2
System.out.println(lru.getCount());
for (Pair p : lru.getAll()) { //7~14
System.out.println(p);
}
System.out.println();
}
}

输出

resize 4 to 8
4
{key=1, val=1}
{key=2, val=2}
{key=5, val=5}
{key=7, val=7} Node{key=2, val=2, hn=n, p=7, n=n}
4
{key=1, val=1}
{key=5, val=5}
{key=7, val=7}
{key=2, val=2} Node{key=5, val=5, hn=n, p=1, n=7}
3
{key=1, val=1}
{key=7, val=7}
{key=2, val=2} {key=1, val=1}
{key=7, val=7}
{key=2, val=2}
{key=7, val=72} 5
{key=1, val=1}
{key=7, val=7}
{key=2, val=2}
{key=7, val=72}
{key=15, val=73} {key=1, val=1}
{key=7, val=7}
{key=2, val=2}
{key=15, val=73}
{key=7, val=72} 5
{key=1, val=1}
{key=7, val=7}
{key=2, val=2}
{key=15, val=73}
{key=7, val=72} resize 8 to 16
count > countLimit , del :Node{key=1, val=1, hn=9, p=n, n=2}
count > countLimit , del :Node{key=2, val=2, hn=n, p=n, n=15}
count > countLimit , del :Node{key=15, val=73, hn=n, p=n, n=7}
8
{key=7, val=72}
{key=8, val=8}
{key=9, val=9}
{key=10, val=10}
{key=11, val=11}
{key=12, val=12}
{key=13, val=13}
{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. 使用 ASP.NET Core MVC 创建 Web API——响应数据的内容协商(七)

    使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 使 ...

  2. VUE,基于vue-cli搭建创建vue项目

    前提:必须安装node.js,官方地址:https://nodejs.org/en/ 然后安装vue-cli 安装方式:cmd命令行安装:打开cmd命令符输入进行全局安装脚手架:npm install ...

  3. PHP 利用PHPExcel到处数据到Excel;还有导出数据乱码的解决方案。

    直接贴代码吧 PHP版本5.6.38 mysql版本5.0 //连接数据库 $mysql_server_name = "*.*.*.*"; $mysql_username=&quo ...

  4. openstack在controller节点使用openstack network agent list不显示计算节点

    问题描述: 做完计算节点neutron的相关配置后,在controller节点查看agent列表,结果如下 发现并没有compute节点 在计算节点上查看status显示failed 在查看日志文件/ ...

  5. SpringBoot启动过程源码分析

    学习博客:SpringBoot时序图分析启动过程

  6. JavaScript-双层for循环打印九九乘法表

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. python爬取图片

    1.导入需要的模块requests,BeautifulSoup,os(用于文件读写). 2.创建一个类,并初始化.   1 2 3 4 5 6 7 8 class BeautifulPicture: ...

  8. 使用脚本进行ansible批量主机的免密配置

    应用场景: 在应用ansible的实际情况中,有一个很现实的问题,ansible是需要对主机做ssh免密登陆的,而挨个对主机做免密是非常的繁琐的,挨个敲IP不仅非常的繁琐而且容易出错,为解决这个问题, ...

  9. SpringBoot2.0 基础案例(17):自定义启动页,项目打包和指定运行环境

    本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一.打包简介 springboot的打包方式有很多种.可以打war ...

  10. router-view 与 动态组件 区别

    提问:router-view 可以页面跳转,使用 is特性 也可以进行页面跳转,有什么区别? 参考链接 https://segmentfault.com/q/1010000010750059