//基于 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. STM32F407外部晶体改为25M后检测不到芯片的解决办法

    问题描述 分享一个之前遇到的STM32F4晶体频率问题,导致单片机死机的解决办法.使用一款新的F4开发板,直接使用的正点原子STM32F407工程模板代码,管脚配置正确,下载到外部晶体为25MHz的开 ...

  2. C#扩展方法学习笔记

    C#扩展方法,简单的理解是不修改原来类的源代码的情况下,为某个类添加某个方法.扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的.它们的第一个参数指定该方法作用于哪个类型,并且该参数以 th ...

  3. 获取主线程Thread.currentThread()

    package seday08.thread; /** * @author xingsir * 主线程 * 线程提供了一个静态方法这个方法会将运行这个方法的线程返回:static Thread cur ...

  4. SSH框架之Spring+Struts2+Hibernate整合篇

    回顾 -Hibernate框架 ORM: 对象关系映射.把数据库表和JavaBean通过映射的配置文件映射起来, 操作JavaBean对象,通过映射的配置文件生成SQL语句,自动执行.操作数据库. 1 ...

  5. CentOS 安装Asp.net Core & FTP服务

    网络设置 确认是否成功连网: ping baidu.com 如果无法上网请检查以下设置 ip link show vim /etc/sysconfig/network-scripts/ipcfg-(看 ...

  6. Centos手动安装PHP

    下载PHP的源码,我下的是7.2版本,看了一下安装的参数太多了,也没有时间依次了解每个参数的意思,直接从网上复制了一个,先尝试安装起来.并记录一下步骤,基本的步骤就是解压.配置.编译.运行.1.下载P ...

  7. python 基础学习笔记(5)--文件操作

    **python 的文件操作** - [ ] 使用python来读写文件是非常简单的操作,我们使用open()来打开一个文件,获取到文件的语柄,然后通过文件语柄就可以进行各种各样的操作了. - [ ] ...

  8. 生产环境Shell脚本Ping监控主机是否存活(多种方法)

    在网上针对shell脚本ping监控主机是否存活的文档很多,但大多都是ping一次就决定了状态,误报率会很高,为了精确判断,ping三次不通再发告警,只要一次ping通则正常.于是,今天中午抽出点时间 ...

  9. Jrebel实现tomcat热部署,遇到的问题以及解决办法,详解

    我的安装的详细过程: 下载Jrebel:  https://github.com/ilanyu/ReverseProxy/releases/tag/v1.4 我的是winx64,所以选择如下的: 下载 ...

  10. sql server判断表存在

    在创建表.更改表结构.删除表或对表进行什么操作之前,一个比较严谨的做法是先判断该表是否已经存在. 在SQL Server中判断一个表是否存在,有两个方法,下面以diso表为例. 方法1 from sy ...