Java小白手撕LRU算法,竟讲的这么通透.

Java小白手撕LRU算法,竟讲的这么通透.,第1张

Java小白手撕LRU算法,竟讲的这么通透. 什么是LRU

LRU全称是Least Recently Used,即最近最久未使用的意思

LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰


图示

映射关系

 

增删改

 


代码问题解释

我们基本实现原理是通过Hashmap+双链表(Entry)实现的.

为什么用hashmap+双链表,而不是单独的双链表?

原因:用hashmap找双链表比较快,容易定位到双链表的具体的单节点,进行 *** 作;而不是每次修改或者删除的时候都需要遍历整条链表为什么定义head节点和tail节点?

原因:用head节点和tail节点更容易 *** 作,尤其是在删除的时候


具体实现方法

    首先需要编写一个Entry类,Entry类里面含有key,value值,还有前驱结点pre和后继节点next,重写构造函数

    其次,在我们的LRUCache类定义一个head节点和tail节点,以及int一个容量池大小capacity和记录我们的链表的长度size,还有一个Hashmap的cache

    我们定义一个initlinkedList()方法,初始化我们的链表,使头尾节点相连

    重写LRUCache的构造函数,赋值capacity并定义Hashmap容量池大小,size=0,调用initlinkedList()方法

    定义addNode()和deleteNode()方法,每次添加节点都添加到头结点之后,删除的话让该几点的前后节点相连

    定义moveToHead()方法,里面包裹着deleteNode()和addNode()方法,方便我们修改已存在的值(因为我们修改已存在的值的时候,需要删除该节点,然后在添加该节点)

    定义get()方法,从hashmap表里查找该key存不存在,如果存在,就调用moveToHead()方法移至头结点,不存在就返回null

    接下来定义put()方法,这个比较难,分情况

      首先我们先get()一下,查看有没有该Entry节点

        如果有该Entry节点,那么我们就重新赋该Entry节点的值,调用moveToHead()方法,返回,结束方法

        如果没有该Entry节点,就new一个新的Entry节点,赋值key,value,调用addNode()方法,并放入Hashmap,size++

        如果size==capacity,也就是链表长度已满,把最后一个节点(不是tail节点,是tail.pre)删掉(因为最后一个节点不常用,所以删掉),并把Hashmap中的该节点也删掉,size--,之后执行上面第2部 *** 作

    测试得出以下语句

public static void main(String[] args) {
        LRUCache cache1 = new LRUCache(2);
        cache1.put(1, 1);
        cache1.put(2, 2);

        System.out.println("get(1)之后的头结点之前的节点: " + cache1.head.next);
        System.out.println("get(1)节点: " + cache1.get(1));
        System.out.println("get(1)之后的头结点之后的节点: " + cache1.head.next);

        cache1.put(3, 3);
        System.out.println("put(3)之后的头结点之后的节点: " + cache1.head.next);

        System.out.println("get(2)节点: " + cache1.get(2));
        System.out.println("get(2)之后的头结点之后的节点: " + cache1.head.next);
    }

        //最终输出
        //get(1)之后的头结点之前的节点: Entry{key=2, value=2}
        //get(1)节点: 1
        //get(1)之后的头结点之后的节点: Entry{key=1, value=1}
        //put(3)之后的头结点之后的节点: Entry{key=3, value=3}
        //get(2)节点: null
        //get(2)之后的头结点之后的节点: Entry{key=3, value=3}

源码

package com.heaboy.mvc;

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

public class LRUCache {
    Entry head, tail;
    int capacity;
    int size;
    Map cache;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        initlinkedList();
        size = 0;
        cache = new HashMap<>(capacity);
    }

    public void put(int key, int value) {
        Entry node = cache.get(key);
        if (node != null) {
            node.value = value;
            moveToHead(node);
            return;
        }
        if (size == capacity) {
            Entry lastNode = tail.pre;
            deleteNode(lastNode);
            cache.remove(lastNode.key);
            size--;
        }

        Entry newNode = new Entry(key, value);
        addNode(newNode);
        cache.put(key, newNode);
        size++;
    }

    public Integer get(int key) {
        Entry node = cache.get(key);
        if (node == null) {
            return null;
        }
        moveToHead(node);
        return node.value;
    }

    private void moveToHead(Entry node) {
        deleteNode(node);
        addNode(node);
    }

    public void addNode(Entry node) {
        head.next.pre = node;
        node.next = head.next;

        node.pre = head;
        head.next = node;
    }

    private void deleteNode(Entry node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }

    private void initlinkedList() {
        head = new Entry();
        tail = new Entry();

        head.next = tail;
        tail.pre = head;
    }


    private static class Entry {
        int key;
        int value;
        Entry pre;
        Entry next;

        public Entry(int key, int value) {
            this.key = key;
            this.value = value;
        }

        public Entry() {
        }

        @Override
        public String toString() {
            return "Entry{" +
                    "key=" + key +
                    ", value=" + value +
                    '}';
        }
    }

    public static void main(String[] args) {
        LRUCache cache1 = new LRUCache(2);
        cache1.put(1, 1);
        cache1.put(2, 2);

        System.out.println("get(1)之后的头结点之前的节点: "+cache1.head.next);
        System.out.println("get(1)节点: "+cache1.get(1));
        System.out.println("get(1)之后的头结点之后的节点: "+cache1.head.next);

        cache1.put(3, 3);
        System.out.println("put(3)之后的头结点之后的节点: "+cache1.head.next);

        System.out.println("get(2)节点: "+cache1.get(2));
        System.out.println("get(2)之后的头结点之后的节点: "+cache1.head.next);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5719829.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存