//基于 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}