LRU hashMap(拉链) + 双向链表 java实现
//基于 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实现的更多相关文章
- How HashMap works in java 2
https://www.javacodegeeks.com/2014/03/how-hashmap-works-in-java.html Most common interview questio ...
- 昨天面试被问到的 缓存淘汰算法FIFO、LRU、LFU及Java实现
缓存淘汰算法 在高并发.高性能的质量要求不断提高时,我们首先会想到的就是利用缓存予以应对. 第一次请求时把计算好的结果存放在缓存中,下次遇到同样的请求时,把之前保存在缓存中的数据直接拿来使用. 但是, ...
- LRU的理解与Java实现
简介 LRU(Least Recently Used)直译为"最近最少使用".其实很多老外发明的词直译过来对于我们来说并不是特别好理解,甚至有些词并不在国人的思维模式之内,比如快速 ...
- 线性链表的双向链表——java实现
.线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素. 链表又可分为: 单链表:每个节点只保留一个引用,该引用指向当前节点的下一个节点,没有引用指向头结点,尾节点的next引用为 ...
- HashMap如何工作 - Java
大多数人应该会同意HashMap是现在面试最喜欢问的主题之一.我和同事常常进行讨论,并很有帮助.现在,我继续和大家讨论. 我假设你对HashMap的内部工作原理感兴趣,并且你已经知道了基本的HashM ...
- 双向链表--Java实现
/*双向链表特点: *1.每个节点含有两个引用,previos和next,支持向前或向后的遍历(除头节点) *2.缺点插入或删除的时候涉及到引用修改的比较多 *注意:下面的双向链表其实也实现了双端链表 ...
- SpringMvc中Hashmap操作遇到 java.util.ConcurrentModificationException: null
代码按照网上修改为类似,还不能解决问题 for (Iterator<String> it = target.keySet().iterator(); it.hasNext(); ) { i ...
- 双向链表-java完全解析
原文:https://blog.csdn.net/nzfxx/article/details/51728516 "双向链表"-数据结构算法-之通俗易懂,完全解析 1.概念的引入 相 ...
- How HashMap works in Java
https://www.javainterviewpoint.com/hashmap-works-internally-java/ How a HashMap Works internally has ...
随机推荐
- 流式计算(一)-Java8Stream
大约各位看官君多少也听说了Storm/Spark/Flink,这些都是大数据流式处理框架.如果一条手机组装流水线上不同的人做不同的事,有的装电池,有的装屏幕,直到最后完成,这就是典型的流式处理.如果手 ...
- Java每日一面(Part2数据库)[19/11/28]
作者:故事我忘了¢个人微信公众号:程序猿的月光宝盒 1.如何设计一个关系型数据库 如上图,首先划分成两大部分: 1.存储部分:类似一个文件系统,把数据存储到一个持久化设备中,如机械硬盘,固态等 ...
- 邬江兴院士:工业互联网安全&拟态防御
尊敬的郑院士.曹书记.张秘书长,各位学术界的同仁们,很高兴在第一届工业互联网学术专题论坛上发言.我今天想谈的问题是工业互联网,这个概念很热,前景也很美好,很诱人.但是我认为工业互联网的安全挑战更严峻, ...
- [转载]——Automatic Tuning of Undo_retention Causes Space Problems (文档 ID 420525.1)
Automatic Tuning of Undo_retention Causes Space Problems (文档 ID 420525.1) 转到底部 In this Document Sy ...
- enable SSL on weblogic
To provision (install) a certificate on the server On the Start menu, click Run, and in the Open box ...
- rpm包安装java jar开机自启
1.安装jdk: rpm -ivh jdk-8u201-linux-x64.rpm 2.配置jdk路径 打开/etc/profile增加以下内容: export JAVA_HOME=/usr/java ...
- 剑指Offer-38.平衡二叉树(C++/Java)
题目: 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 分析: 可以从根节点开始遍历每一个节点,求得节点左右子树的最大高度,判断是不是平衡二叉树.这样做的问题在于会重复遍历节点,造成不必要的浪费. 所 ...
- PHP 实现精确统计在线人数功能
有需要学习交流的友人请加入交流群的咱们一起,有问题一起交流,一起进步!前提是你是学技术的.感谢阅读! 点此加入该群jq.qq.com PHP对session对象的封装的很好,根据HTTP协议,每个范 ...
- Eureka工作原理及它和ZooKeeper的区别
1.Eureka 简介: Eureka 是 Netflix 出品的用于实现服务注册和发现的工具. Spring Cloud 集成了 Eureka,并提供了开箱即用的支持.其中, Eureka 又可细分 ...
- 数据、模型、IT系统认知
数据.模型.IT系统认知 量化投资定义 量化投资主要是指通过数理模型来实现投资理念,由计算机产生交易策略的一种投资方法. 量化投资是一种方法论,而不是具体的交易策略. 通常与基本面.技术面分析相结合. ...