HashMap+双向链表实现LRU

LRU即是Least Recently Used,即最近最少使用,选择最近最久未使用的数据将其淘汰。

最简单的想法是使用先进先出(FIFO)的方式来实现,通过双向链表来实现

因为链表插入和删除快,但是查询慢 ,而 HashMap的查询速度快很多,所以除了构建一个双向链表,还构建了一个哈希map,用于快速查询对应key的节点,用空间换时间

每次key被访问的时候,把被访问到的key移到头节点,这样当添加新节点的时候,直接添加到头节点即可,如果缓存已满,直接删除尾节点就可以了

jdk里有提供现成的LinkedHashMap,采用HashMap+链表的方式自己实现了一下

/**
 * @Description
 * @Author chenpp
 * @Date 2019/11/21 17:50
 * 定义双向链表的节点
 */
public class Node<K,V> {
    private K key;
    private V value;
    private Node next;
    private Node pre;
    public Node(K key,V value){
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    public Node getPre() {
        return pre;
    }

    public void setPre(Node pre) {
        this.pre = pre;
    }
}

/**
 * @Description
 * @Author chenpp
 * @Date 2019/11/21 17:56
 * 定义一个双向链表
 */
public class DoubleLinkedList<K,V> {

    transient Node<K,V> first;
    transient Node<K,V> last;
    /**
     * 当前链表里的节点数目
     * */
    transient int size;

    /**
     * 添加节点到链表头部
     * */
    public void addFirst(Node node) {
        if (first == null) {
            first = node;
            last = node;
        } else {
            Node oldFirst = first;
            oldFirst.setPre(node);
            node.setNext(oldFirst);
            first = node;
        }
        size++;
    }

    /**
     * 删除链表里已有的node节点
     *
     * */
    private Node removeNode(Node node){
        if(node == null){
            return null;
        }
        Node preNode = node.getPre();
        Node nextNode = node.getNext();
        if(preNode != null) {
            preNode.setNext(nextNode);
        }else{
            first = nextNode;
        }
        if(nextNode != null){
            nextNode.setPre(preNode);
        }else{
            last = preNode;
        }
        size--;
        return node;
    }

    /**
     * 将链表指定节点node移动到头部
     *
     * */
    public Node moveToHead(Node node){
        if(node == first){
            return node;
        }
        if( node == last){
            last = node.getPre();
        }else{
            node.getNext().setPre(node.getPre());
        }
        node.getPre().setNext(node.getNext());
        node.setPre(null);
        node.setNext(first);
        first = node;
        return node;
    }

    /**
     * 删除最后一个节点
     * 并返回删除的节点
     *
     * */
    public Node removeLast(){
        Node node = last;
        removeNode(last);
        return node;
    }

    public int size(){
        return size;
    }
}

/**
 * @Description
 * @Author chenpp
 * @Date 2019/11/21 17:52
 * 
 */
public class LRUCache<K,V> {

    private HashMap<K, Node<K,V>> linkedHashMap;
    private int capacity;
    private DoubleLinkedList linkedList;

    public LRUCache(int capacity){
        this.capacity = capacity;
        linkedList = new DoubleLinkedList();
        linkedHashMap = new HashMap<K, Node<K,V>>();
    }


    /**
     * 访问缓存指定的key
     * 将对应的node移动到链表头部
     *
     * */
    public V get(K k){
        Node<K,V> node = linkedHashMap.get(k);
        if(node == null){
            return null;
        }
        //将访问的节点移动到头节点
        linkedList.moveToHead(node);
        return node.getValue();
    }

    /**
     * 新增node节点到链表头部
     * 如果缓存超过最大容量,则删除尾部节点
     *
     * */
    public void put(K key,V val){
        Node node = linkedHashMap.get(key);
        //如果节点的key本来就存在,则直接移动到头节点
        if(node != null){
            node.setValue(val);
            node = linkedList.moveToHead(node);
        }else{
             node = new Node(key,val);
            //如果key不存在,则新增节点到头部
            if(linkedList.size() >= capacity){
                //移除访问时间最远的节点
                Node last = linkedList.removeLast();
                linkedHashMap.remove(last.getKey());
            }
            linkedList.addFirst(node);
        }
        //更新hashMap里的值
        linkedHashMap.put(key,node);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node node = linkedList.first;
        while(node != null){
            sb.append(String.format("%s:%s ", node.getKey(),node.getValue()));
            node = node.getNext();
        }
        return sb.toString();
    }


}

测试一下:

/**
 * @Description
 * @Author chenpp
 * @Date 2019/11/21 19:49
 */
public class Test {
    public static void main(String[] args) {
        LRUCache<String,String> cache = new LRUCache<String, String>(5);
        cache.put("a","111");
        cache.put("b","2121");
        cache.put("c","3231");
        cache.put("d","2122");
        cache.put("e","2131");
        cache.get("3");//e d c b a
        System.out.println(cache.toString());
        cache.get("a");// a e d c b
        System.out.println(cache.toString());
        cache.put("f","13123");// f a e d c
        System.out.println(cache.toString());
        cache.get("b");
        System.out.println(cache.toString());
        cache.get("a");// a f e d c
        System.out.println(cache.toString());
        cache.put("g","g");// g a f e d
        System.out.println(cache.toString());
        cache.put("c","31");// c g a f e
        System.out.println(cache.toString());
        cache.get("d");
        System.out.println(cache.toString());
        cache.get("c");
        System.out.println(cache.toString());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值