链表模拟LRU内存

本文详细介绍了如何使用单链表和双向链表实现LRU(Least Recently Used)缓存淘汰策略的缓存调入功能。通过自定义节点结构,根据缓存大小判断是否已满,实现节点的头插法插入,并在缓存满时移除最近最少使用的节点。还提供了测试代码以验证功能的正确性。
摘要由CSDN通过智能技术生成

单链表实现LRU淘汰策略中缓存调入功能

实现思路

以自定义结点作为缓存当中的内容,进行两步判断:

  1. 判断要调入缓存的内容是否已经存在于缓存当中
    – 如果已经存在,则从原本位置摘下此结点,以头插法加进链表
    – 如果不存在于链表当中,则需要新增进链表,此时需判满
    2.在要调入内容不存在于缓存的情况下,判断当前缓存是否已满:
    – 如果没有满,则直接头插法加入;
    –如果缓存已满,需要从链表最后摘下一个结点,腾出空间之后,头插法加入新结点

具体实现

  1. 自定义结点结构以及定义缓存大小
    缓存通过静态成员变量存储,记录当前尚余的缓存空间。
public static int size = 100
class Node {
	//数据域与指针域
    int value;
    Node next;
    //单参/双参构造方法
    public Node(int value) {
        this.value = value;
    }
    public Node(int value, Node next) {
        this.value = value;
        this.next = next;
    }
	//重写toString以便验证结果
    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }
}
  1. 判断该结点是否处于当前内存当中
    通过遍历即可实现,如果存在此结点就返回其前驱结点,如果不存在则返回null;
    因为此处是单链表实现,在判断之后需要进行摘链等操作,为避免找前驱结点再次进行遍历,直接返回的是前驱结点。头结点由于没有前驱,单独进行判断。
public static Node findPrevious(Node head, Node node) {
        //有node就返回前驱,没有就返回null,head单独判断
        Node x = head;
        if (head == node) {
            return head;
        } else {
            while (x.next != null) {
            	//data域相同的结点认为相同
                if (x.next.value == node.value) return x;
                x = x.next;
            }
        }
        return null;
    }
}
  1. 将移除尾部结点定义成一个方法
    遍历到链表最后并摘下最后一个结点
public static void removeTailNode(Node head) {
        Node x = head;
        while (x.next.next != null) {
            x = x.next;
        }
        //删除尾部节点
        x.next = null;
    }

4.实现整个新增逻辑
要注意的是,头插之后产生的是个新的链,如果此方法返回值为void,那么在调用处得到的不是我们改动之后的链。

public static Node insert(Node head, Node node) {
        //向链表head中添加node结点
        //判存在的同时取其前驱
        Node pre = findPrevious(head, node);
        if (pre != null) {
            //该结点存在,先删再头插
            pre.next = pre.next.next;
            node.next = head;
            head = node;
        } else {
            //如果不存在于链表当中,则进行判满及后续处理
            if (size == 0) {
                //摘下最后结点
                removeTailNode(head);
                //头插
                node.next = head;
                head = node;
            } else {
                //头插
                node.next = head;
                head = node;
                size--;
            }
        }
        return head;
    }

5.测试代码

public static void main(String[] args) {
        Node node = new Node(101);
        Node node1 = new Node(36);
        Node node2 = new Node(100);

        //头插法创建99个结点
        Node head = null;
        for (int i = 1; i <= 99; i++) {
            head = new Node(i, head);
            (size)--;
        }
        //打印刚创建好的链表
        printList(head);
        //调入36结点
        head = insert(head, node1);
        printList(head);
        //调100,101结点
        head = insert(head, node2);
        head = insert(head, node);
        printList(head);
    }

6.运行效果
在这里插入图片描述

双向链表实现LRU淘汰策略中缓存调入功能

实现思路与单链表相同,唯一不同的是对于结点的处理

具体实现

1.结点类定义

/*自定义双链表结点*/
class Node<T>{
	//泛型保证数据域数据的灵活性
    T data;
    Node pre;
    Node next;

    public Node(T data) {
        this.data = data;
    }

    public Node(T data, Node pre, Node next) {
        this.data = data;
        this.pre = pre;
        this.next = next;
    }
	//方便显示
    @Override
    public String toString() {
        return "{" + data +'}';
    }
	//方便比较结点内容
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Node)) return false;
        Node<?> node = (Node<?>) o;
        return Objects.equals(data, node.data);
    }
}

2.封装的LRU类

  • 将首尾结点,缓存容量,添加结点的方法都封装在类中提供
  • 其中频繁使用的头插/移除末尾结点的操作,封装成方法进行调用。
  • 添加首尾结点是为了保证操作的一致性。
class LRU {
   private int size=100;
   private Node head=new Node("head");
   private Node tail=new Node("end");

   public LRU(int size) {
       //创建的时候就必须指定缓存大小
       this.size = size;
       //创建的两个边界节点首先接上链,才能够保证操作的一致性
       this.getHead().next=this.getTail();
   }
   public Node getHead() {
       return head;
   }
   public Node getTail() {
       return tail;
   }
   public int getSize() {
       return size;
   }

   public void addNode(Node node){
       //判断是否存在
       Node x=isExsist(node);
       if(x!=null){
           //如果该节点存在于当前缓存当中,删了再头插
           removeNode(x);
           insertAtFirst(x);
       }else{
           //如果该结点不存在于当前缓存中,判满
           if(size!=0){
               //没满直接头插,内存容量-1
               //这里用node操作不要用x,x如果被判空会变为null
               insertAtFirst(node);
           }else{
               //满了,删掉最后一个结点再头插
               removeNode(tail.pre);
               insertAtFirst(node);
           }
       }
   }

   public void insertAtFirst(Node node){
       //将node结点头插在head结点之后
       node.next=head.next;
       node.pre=head;
       node.pre.next=node;
       node.next.pre=node;
       size--;
   }
   public void removeNode(Node node){
       //摘下指定结点node,要删第一个结点传head.next,删最后一个结点传tail.pre
           node.next.pre = node.pre;
           node.pre.next = node.next;
           //结点数量管理
           size++;
          /* node.next = null;
           node.pre = null;*/
   }
   public  Node isExsist(Node node){
       //判断当前链表当中是否存在该结点,存在就返回该结点,不存在就返回null
       //直接正向遍历并寻找该结点
       Node x=head.next;
       while(x!=tail){
           //如果存在内容一样的,返回结点当前的位置
           //node.data==x.data,这里用==的话会造成比较的是左右两个对象的地址,同样的字符串可能因地址不同被插进去
           if(node.equals(x)) return x;
           x=x.next;
       }
       //如果遍历了都没找到,返回null
       return null;
   }
   public void print(Node head){
       //从头打印链表
       Node x=head;
       while(x!=tail){
           System.out.print(x+"--");
           x=x.next;
       }
       System.out.println("{tail}");
   }
}
  1. 测试类
public static void main(String[] args) {
        //新建对象并设定缓存大小
        LRU lru=new LRU(10);

        for(int i=0;lru.getSize()>0;i++){
            lru.insertAtFirst(new Node(i));
        }
        //打印原链表
        lru.print(lru.getHead());
        //新建结点
        Node node1=new Node(5);
        Node node2=new Node("hello");

        lru.addNode(node1);
        lru.print(lru.getHead());
        lru.addNode(node2);
        lru.print(lru.getHead());
    }

4.运行结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值