//基于 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. wireshark抓包如何查看视频分辨率和码率

    本文简单介绍如何查看Wireshark抓取pcap包,其视频码流的分辨率和码率. 查看分辨率 我们打开一个抓取的pcap文件,找到标记为SPS(Sequence Parameter Set)的数据包. ...

  2. 【Leetcode 做题学算法周刊】第五期

    首发于微信公众号<前端成长记>,写于 2019.12.06 背景 本文记录刷题过程中的整个思考过程,以供参考.主要内容涵盖: 题目分析设想 编写代码验证 查阅他人解法 思考总结 目录 10 ...

  3. Android OkHttp + Retrofit 断点续传

    本文链接 前面我们已经知道如何使用OkHttp+Retrofit下载文件. 下载文件时,可能会遇到一些意外情况,比如网络错误或是用户暂停了下载. 再次启动下载,如果又要从头开始,会白白浪费前面下载好的 ...

  4. IPIP.net识别客户端真实访问地址,具体到国家,省,市

    这个IP库实测还是比较准确的,免费版的可以具体到国内城市,国外只能到国家名称,免费版的自己定期更新Ip数据库即可. 以下为C#调用代码 class Program { static void Main ...

  5. Execl数据上传到数据库

    =============================================================asp.net================================ ...

  6. 使用create-react-app+react-router-dom+axios+antd+react-redux构建react项目

    1.安装.构建 # 全局安装 npm install -g create-react-app # 构建一个my-app的项目 npx create-react-app my-app cd my-app ...

  7. IDEA新建Spring配置文件的方法

    IDEA创建Spring Config 选择项目文件右键 输入文件名称即可 applicationContext.xml

  8. 【Java基础】接口和抽象类之间的对比

    Java 中的接口和抽象类之间的对比 一.接口 Interface,将其翻译成插座可能就更好理解了.我们通常利用接口来定义实现类的行为,当你将插座上连接笔记本的三角插头拔掉,换成微波炉插上去的时候,你 ...

  9. MySQL数据库:运算符

    运算符 比较运算符: > 大于 < 小于 >= 大于等于 <= 小于等于 = 等于 != 不等于 范围运算符: between...and...(包含边界值) 类似于 > ...

  10. git 删除文件/移动文件

    1.git rm 删除文件 git restore --file (git老版本:git checkout) git rm --file(本地和管理都已删除) git rm --cached file ...