Java语言实现LRU(最少最近使用)缓存淘汰算法

LRU(least recently use)最少最近使用算法,是缓存淘汰中常用的算法,实际使用要求是:

1.把最常使用的放前面,不常使用的放后面,至于这里的前面和后面会在后面说到。

2.要求存取的时间复杂度是O(1)。

3.存数据的时候,如果容量已满,则去除最后不常使用的数据,再把新数据插入,插入的数据位于前面。如果有该数据则移动到前面。

4.取数据的时候,没有则返回-1,有则返回该数据,并将该数据放在前面。

这里借助hash表和双链表来实现,因为要求时间复杂度是常数级,所以使用hash表存取数,要实现快速移动数据,这里采用双链表,不能采用单链表,单链表不能获得前驱和后继结点指针,必须遍历获得。

而双链表本身结点就保存所以不需要遍历。借助这样的数据结构我们可以知道上文说的前面其实就是链表头,后面就是链表尾。

代码如下所示:

package com.algorithm;

import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

/**
 * LRU-least recently use:最少最近使用
 * 1.get(key),有则返回对应的value值,否则返回-1,将该节点移动到双链表的头部。
 * 2.put(key,value),如果双链表容量满了,则移除链表尾部节点,将新节点放到头结点位置,如果没有满,直接放到头部位置。
 * 3.要求时间复杂度为O(1)./
 */
public class LRUCache {

    public static void main(String[] args) {
        LRUCache cache = new LRUCache(3);
        cache.set("a", 1);
//        System.out.println(cache.get("a"));
//        System.out.println(cache.get("b"));
        cache.set("b", 2);
        cache.set("c", 3);
        cache.pirnt();
        cache.set("d", 4);
        cache.pirnt();
        cache.get("b");
        cache.pirnt();
//        System.out.println(cache.get("a"));


    }


    int capacity;
    Map<String, DoubleLinkedListNode> container;
    DoubleLinkedListNode head, tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.container = new HashMap<>(capacity);
        head = new DoubleLinkedListNode();
        head.pre = null;
        tail = new DoubleLinkedListNode();
        tail.next = null;
        head.next = tail;
        tail.pre = head;

    }

    public void pirnt() {
        DoubleLinkedListNode node = head;
        StringJoiner stringJoiner = new StringJoiner("-");
        while (node.next != null) {
            if (node.next.value!=null){
                stringJoiner.add(node.next.value.toString());
            }
            node = node.next;
        }
        System.out.println(stringJoiner.toString());
    }

    public void set(String key, Integer value) {
        DoubleLinkedListNode node = this.container.get(key);
        if (node == null) {//新节点
            node = new DoubleLinkedListNode();
            node.key = key;
            node.value = value;
            if (container.size() >= capacity) {
                System.out.println("双向链表已满,移除尾部元素!");
                DoubleLinkedListNode del = removeTail();
                this.container.remove(del.key);
            }
            this.container.put(key, node);
            addFromHead(node);
        } else {//老节点,移动到头部
            moveToHead(node);
        }

    }

    public Integer get(String key) {
        DoubleLinkedListNode node = this.container.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }

    private void moveToHead(DoubleLinkedListNode node) {
        removeNode(node);
        addFromHead(node);
    }

    private DoubleLinkedListNode removeTail() {
        DoubleLinkedListNode node = tail.pre;
        removeNode(node);
        return node;
    }

    private void removeNode(DoubleLinkedListNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

    private void addFromHead(DoubleLinkedListNode cur) {
        cur.pre = head;
        cur.next = head.next;//下面代码的顺序一定不能颠倒,必须要对,否则指向会出问题。
        head.next.pre = cur;
        head.next = cur;
    }


    // 循环链表
    class DoubleLinkedListNode {
        String key;
        Integer value;
        DoubleLinkedListNode pre;
        DoubleLinkedListNode next;
    }
}


 特别需要注意的是:在插入元素的时候,双链表指针的移动需要特别注意操作顺序,代码中已经特别标明:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值