1.原理
LRU(Least Recently Used)是一种常见的
页面置换算法
,在计算中,所有的文件操作都要放在内存中进行,然而计算机内存大小是固定的,所以我们不可能把所有的文件都加载到内存,因此我们需要制定一种策略对加入到内存中的文件进项选择。
LRU的设计原理就是,当数据在最近一段时间经常被访问,那么它在以后也会经常被访问。这就意味着,如果经常访问的数据,我们需要然其能够快速命中,而不常访问的数据,我们在容量超出限制内,要将其淘汰。
2.实现思路
大致思路:
构建双向链表节点ListNode,应包含key,value,prev,next这几个基本属性
对于Cache对象来说,我们需要规定缓存的容量,所以在初始化时,设置容量大小,然后实例化双向链表的head,tail,并让head.next->tail tail.prev->head,这样我们的双向链表构建完成
对于get操作,我们首先查阅hashmap,如果存在的话,直接将Node从当前位置移除,然后插入到链表的首部,在链表中实现删除直接让node的前驱节点指向后继节点,很方便.如果不存在,那么直接返回Null
对于put操作,比较麻烦。
3.java代码实现
package
com.facishare.bi.stat.util.LRU
;
import
java.util.HashMap
;
public class
LRUCache{
private int
capacity
;
private
HashMap<String
,
Node>
cache
;
private
Node
head
;
private
Node
tail
;
public
LRUCache
(
int
capacity){
this
.
capacity
= capacity
;
cache
=
new
HashMap<String
,
Node> (capacity)
;
head
=
new
Node(
"head"
,
"head"
)
;
tail
=
new
Node(
"tail"
,
"tail"
)
;
head
.
next
=
tail
;
tail
.
pre
=
head
;
}
/**
* put方法
* 1。判断这个node在cache中是否存在,如果不存在,即插入新node
* 2。插入新node之前要判断一下,cache的容量是否已经超出最大容量,如果超出,必须先移除链表尾部的node,之后再插入新node
* 3。如果此node已经存在,需要将此node从原来的未知移除,再插入链表头部
*
@param
key
*
@param
value
*/
public void
put
(String key
,
Object value){
Node node =
cache
.get(key)
;
if
(node==
null
){
if
(
cache
.size() >
capacity
){
removeOldestNode()
;
}
Node newNode =
new
Node(key
,
value)
;
addNodeToHead(newNode)
;
cache
.put(key
,
newNode)
;
}
if
(node!=
null
){
removeThisNode(node)
;
addNodeToHead(node)
;
}
}
/**
* delete方法
*
@param
key
*/
public void
delete
(String key){
Node node =
cache
.get(key)
;
if
(node!=
null
){
removeThisNode(node)
;
cache
.remove(key)
;
}
if
(node==
null
){
System.
out
.println(
"此节点不存在!"
)
;
}
}
/**
* change方法
* 1。通过key获取node信息,如果不为空,更新node信息,然后将此node从当前未知移除,插入链表头部
* 2。如果为空,调用put方法将此node插入链表头部
*
@param
newNode
*/
public void
change
(Node newNode){
Node oldNode=
cache
.get(newNode.
key
)
;
if
(oldNode !=
null
){
oldNode.
value
= newNode.
value
;
removeThisNode(oldNode)
;
addNodeToHead(oldNode)
;
}
if
(oldNode ==
null
){
put(newNode.
key
,
newNode.
value
)
;
}
}
/**
* get方法
* 1。通过key获取node,如果node不为空,将此node从当前位置移除,插入链表头部,返回node的value
* 2。如果为空,返回null
*
@param
key
*
@return
*/
public
Object
get
(String key){
Node node =
cache
.get(key)
;
if
(node!=
null
){
removeThisNode(node)
;
addNodeToHead(node)
;
return
node.
value
;
}
return null;
}
//移除当前node
private void
removeThisNode
(Node node) {
node.
pre
.
next
= node.
next
;
node.
next
.
pre
= node.
pre
;
}
//将node插入链表头部
private void
addNodeToHead
(Node newNode) {
newNode.
next
=
head
.
next
;
head
.
next
.
pre
= newNode
;
head
.
next
= newNode
;
newNode.
pre
=
head
;
}
//移除链表尾部的node
private void
removeOldestNode
() {
String oldKey =
tail
.
pre
.
key
;
tail
.
pre
.
pre
.
next
=
tail
;
tail
.
pre
=
tail
.
pre
.
pre
;
cache
.remove(oldKey)
;
}
@Override
public
String
toString
() {
StringBuilder sb =
new
StringBuilder()
;
Node node =
head
;
while
(node !=
null
) {
sb.append(String.
format
(
"%s:%s "
,
node.
key
,
node.
value
))
;
node = node.
next
;
}
return
sb.toString()
;
}
public static void
main
(String[] args) {
LRUCache lru =
new
LRUCache(
5
)
;
lru.put(
"1"
,
"a"
)
;
lru.put(
"2"
,
"b"
)
;
lru.put(
"3"
,
"c"
)
;
lru.put(
"4"
,
"d"
)
;
lru.put(
"5"
,
"e"
)
;
System.
out
.println(
"原始链表为:"
+lru.toString())
;
lru.get(
"4"
)
;
System.
out
.println(
"获取key为4的元素之后的链表:"
+lru.toString())
;
lru.put(
"6"
,
"f"
)
;
System.
out
.println(
"新添加一个key为6之后的链表:"
+lru.toString())
;
lru.delete(
"3"
)
;
System.
out
.println(
"移除key=3的之后的链表:"
+lru.toString())
;
}
}