LRU算法,实现了put,remove(int),remove(),setMaxLength(int)四种方法。
- put:put方法为向缓存中加入一个元素,若元素存在,则提到头结点。
- remove int:移除指定缓存数据,并返回移除结果(boolean)。
- remove:移除最后一个元素。
- seMaxLength:设置缓存的大小。若新值比旧有元素的空间小,要删除末尾元素。
- 这里的写法我避免了一种情况,就是缓存空间大小为0或1的情况。首先缓存实现也没有只能放0或1个元素的情况;就算假设有这种情况,加个判断即可,我这里实现了仅仅是基础代码,欢迎大佬修改指正。
- 还有就是,我这里有很多冗余代码,没有抽离出来单独放到一个方法中(比如设置头结点等),是为了方便理解
1. 维护的内部类
/* 维护一个双向链表 */
static class Node {
Node pre;
Node next;
int val;
public Node(int val) {
this.val = val;
}
}
2. 构造代码块与构造方法
Node head;
Node tail;
HashMap<Integer, Node> cacheMap;
private int maxLength;
public NewLRU(int n) {
if (n <= 1) {
this.maxLength = 8; // 默认为8
} else {
this.maxLength = n;
}
cacheMap = new HashMap<>(maxLength);
}
3. put方法,加入一个元素,没有就添加,有就直接提到head,多出来就删除。注意别忘了cacheMap的增删
/* 向LRU结构中中添加val值 */
public void put(int val) {
if (head == null) {
// 此时没有元素,直接添加
Node node = new Node(val);
head = tail = node;
cacheMap.put(val, node);
} else {
// 有元素,判断是否已经有了
if (cacheMap.containsKey(val)) {
Node node;
// 若 == head 则不用更新
if ((node = cacheMap.get(val)) != head) {
if (node == tail) { // 若刚好为尾结点(此处已经不为head,说明至少2个元素)
tail = node.pre;
// 断开
tail.next = null;
} else { // 此处既不是head也不是tail,说明至少3个元素
// 断开
node.pre.next = node.next;
node.next.pre = node.pre;
}
node.pre = null;
// 插到头,并更新head
node.next = head;
head.pre = node;
head = node;
}
} else { // 没有元素,则创建并插入,插入结束后判断是否超过maxLength
Node node;
// 判断数量,如果够maxLength个,就直接修改tail.val并插到头结点,节省创建对象时间
if (cacheMap.size() >= maxLength) {
node = tail;
// 可以不新建Node,但一定要在cache中移除此节点,否则会出现key和对应节点.val不一致的情况
cacheMap.remove(node.val);
node.val = val;
// 更新tail
tail = node.pre;
// 尾结点断开,并插到头
tail.next = null;
node.pre = null;
} else { // 不足maxLength个就创建并插入头
node = new Node(val);
}
// 插入head前
node.next = head;
head.pre = node;
head = node;
cacheMap.put(val, node);
}
}
}
4. 移除指定元素
/* 移除指定元素 */
public boolean remove(int val) {
if (cacheMap.containsKey(val)) {
Node node = cacheMap.get(val);
cacheMap.remove(val);
if (node == head) { // 可能只有1个元素
head = head.next;
if (head != null) head.pre = null;
} else if (node == tail) { // 不为head,且为tail,至少2个元素
tail = tail.pre;
tail.next = null;
} else { // 至少3个元素
// 断开
node.pre.next = node.next;
node.next.pre = node.pre;
node.pre = null;
node.next = null;
}
return true;
}
return false;
}
5. 移除最后一个元素
/* 移除一个元素 */
public Integer remove() {
if(head == null) {
return null;
}
int res = tail.val;
cacheMap.remove(res);
if (tail == head) {
tail = head = null;
return res;
}
// 否则说明有至少2个元素
Node tmp = tail;
tail = tail.pre;
tmp.pre = null;
tail.next = null;
return res;
}
6. 更新缓存大小
/* 更新最大值 */
public void setMaxLength(int newLength) throws Exception {
if (newLength <= 1) {
throw new Exception("缓存空间应该设置大于1");
}
// 若新长度更小,则要考虑删除元素
if (newLength < cacheMap.size()) {
int cnt = cacheMap.size() - newLength;
while (cnt-- > 0) {
cacheMap.remove(tail.val);
tail = tail.pre;
}
// 断开
tail.next.pre = null;
tail.next = null;
}
this.maxLength = newLength;
}
测试结果
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Node tmp = head;
while (tmp != null) {
sb.append(tmp.val).append(" -> ");
tmp = tmp.next;
}
sb.append("null\n cacheMap: {");
if (cacheMap.size() > 0) {
for (Map.Entry<Integer, Node> entry : cacheMap.entrySet()) {
sb.append(entry.getKey()).append('=').append(entry.getValue().val).append(',').append(' ');
}
sb.delete(sb.length() - 2, sb.length());
sb.append('}');
} else {
sb.append("null}");
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
NewLRU lru = new NewLRU(5);
System.out.println(lru.toString()); // 54321
lru.put(1);
System.out.println(lru.toString()); // 15432
lru.put(4);
System.out.println(lru.toString()); // 41532
lru.put(6);
System.out.println(lru.toString()); // 64153
lru.remove(1);
System.out.println(lru.toString()); // 6453
lru.setMaxLength(3);
System.out.println(lru.toString()); // 645
lru.setMaxLength(5);
lru.put(5);
System.out.println(lru.toString()); // 564
lru.put(6);
System.out.println(lru.toString()); // 654
lru.put(7);
System.out.println(lru.toString()); // 7654
lru.put(5);
System.out.println(lru.toString()); // 5764
lru.remove();
System.out.println(lru.toString()); // 576
lru.remove();
System.out.println(lru.toString()); // 57
lru.remove();
System.out.println(lru.toString()); // 5
lru.remove();
System.out.println(lru.toString()); // null
}
// 结果如下所示
null
cacheMap: {null}
1 -> null
cacheMap: {1=1}
4 -> 1 -> null
cacheMap: {1=1, 4=4}
6 -> 4 -> 1 -> null
cacheMap: {1=1, 4=4, 6=6}
6 -> 4 -> null
cacheMap: {4=4, 6=6}
6 -> 4 -> null
cacheMap: {4=4, 6=6}
5 -> 6 -> 4 -> null
cacheMap: {4=4, 5=5, 6=6}
6 -> 5 -> 4 -> null
cacheMap: {4=4, 5=5, 6=6}
7 -> 6 -> 5 -> 4 -> null
cacheMap: {4=4, 5=5, 6=6, 7=7}
5 -> 7 -> 6 -> 4 -> null
cacheMap: {4=4, 5=5, 6=6, 7=7}
5 -> 7 -> 6 -> null
cacheMap: {5=5, 6=6, 7=7}
5 -> 7 -> null
cacheMap: {5=5, 7=7}
5 -> null
cacheMap: {5=5}
null
cacheMap: {null}
Process finished with exit code 0