Java教程

LRU算法

本文主要是介绍LRU算法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

LRU算法

简介

​ LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的数据予以淘汰。

​ 很多缓存中采用这种算法。

一:LinkHashMap实现LRU算法

LinkedHashMap 会对数据使用来排序,这样服务LRU算法的最少使用的思想了

public class LRUCacheDemo<k, v> extends LinkedHashMap<k, v> {

  private int capacity;

  public LRUCacheDemo(int capacity) {
    // assesOrder  我们就清楚了当accessOrder设置为false时,会按照插入顺序进行排序,当accessOrder为true是,会按照访问顺序
    super(capacity, 0.75f, true);
    this.capacity = capacity;
  }
  
  @Override
  protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
    return super.size() > capacity;
  }

  public static void main(String[] args) {
    LRUCacheDemo lruCacheDemo = new LRUCacheDemo<>(3);
    lruCacheDemo.put(1, "a");
    lruCacheDemo.put(2, "b");
    lruCacheDemo.put(3, "c");

    System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());

    lruCacheDemo.put(4, "d");
    System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());
    
    lruCacheDemo.get(3);
    System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());
    
    lruCacheDemo.put(5, "e");
    System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());

  }
}

我们定义一个cacheDemo类继承这个LinkedHashMap ,Cache类构造器,提供capacity 参数,作为LRU清理缓存的边界。

复写removeEldestEntry 方法,当我们缓存的容量达到设定的边界的时候,清理最少使用的缓存。

特别说明:调用父类构造的时候,有一个参数assesOrder 这个参数的作用就是,当accessOrder设置为false时,会按照插入顺序进行排序,当accessOrder为true是,会按照访问顺序,这里我们最为缓存就设置为true。

测试结果:

lruCacheDemo.keySet() = [1, 2, 3]
lruCacheDemo.keySet() = [2, 3, 4]
lruCacheDemo.keySet() = [2, 4, 3]
lruCacheDemo.keySet() = [4, 3, 5]

我们插入a,b,三个值,插入第四个的时候,第一个清理了,当我们访问3的时候,3排队到最后了,2是最少访问的。继续插入5,2就退出了。

二:手写LRU缓存

这个模式是借鉴了,AQS同步队列的,通过hash保证访问速度,链表中的头尾节点来记录最近访问,和最少访问的数据。

package com.leven.demoplus.javase.algorithm;

import java.util.HashMap;

/**
 * 基于lru算法的缓存设计:最近最少使用
 */
public class LRUCacheDemo2 {
    // 缓存容量
    private int capacity;
    // hashmap用于存数据
    private HashMap<Integer,Node<Integer,Integer>> map;
    // 链表数据结构维护访问次数:1:最近访问的放在头节点 2:缓存容量达到限制的时候,移除链表的尾节
    private DoubleLinkList doubleLinkList;

    public LRUCacheDemo2(int capacity) {
        this.capacity = capacity;
        map = new HashMap();
        doubleLinkList = new DoubleLinkList();
    }

    public int get(int key){
        if (!map.containsKey(key)){
            return -1;
        }
        Node<Integer, Integer> node = map.get(key);
        // 访问的数据node,移动到链表的头head
        doubleLinkList.removeNode(node);
        doubleLinkList.addHead(node);
        return node.value;
    }

    public void  put(Integer key, Integer value){
        if (map.containsKey(key)){
            Node<Integer, Integer> node = map.get(key);
            node.value = value;

            // 新加入数据加入头节点
            doubleLinkList.removeNode(node);
            doubleLinkList.addHead(node);
        }else {
            // 缓存空间满了
            if (map.size() >= capacity){
                // 容量达到限制,淘汰尾节点数据
                Node lastNode = doubleLinkList.getLastNode();
                doubleLinkList.removeNode(lastNode);
                map.remove(lastNode.key);
            }
            Node<Integer, Integer> newNode = new Node<>(key, value);
            map.put(key,newNode);
            doubleLinkList.addHead(newNode);
        }
    }

    class DoubleLinkList<k,v>{
       Node<k,v> head;
       Node<k,v> tail;


        public DoubleLinkList() {
            head = new Node<>();
            tail = new Node<>();
            head.next = tail;
            tail.pre = head;
        }

        // 添加到头
        public void addHead(Node<k,v> node){
            node.next = head.next;
            node.pre = head;

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

        public void removeNode(Node<k,v> node){
            node.next.pre = node.pre;
            node.pre.next = node.next;
            node.pre = null;
            node.next = null;
        }

        public Node<k,v> getLastNode(){
            return tail.pre;
        }


    }

    class Node<k,v>{
        k key;
        v value;
        Node<k,v> pre;
        Node<k,v> next;

        public Node() {
            this.pre = this.next = null;
        }

        public Node(k key, v value) {
            this.key = key;
            this.value = value;
        }
    }

  public static void main(String[] args) {
      LRUCacheDemo2 lruCacheDemo = new LRUCacheDemo2(3);
      lruCacheDemo.put(1,1);
      lruCacheDemo.put(2, 2);
      lruCacheDemo.put(3, 3);

      // 1:预期:容量达到限制,淘汰尾节点1,打印:[1, 2, 3]
      System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());

      // 2:预期:容量达到限制,淘汰尾节点1,打印:[2, 3, 4]
      lruCacheDemo.put(4, 4);
      System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());

      // 3:预期:访问4的数据,最小访问,加入头节点,打印:[2, 3, 4]
      lruCacheDemo.get(2);
      System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());

      // 4:预期:加入5,达到限制,淘汰最久访问的缓存3(如果不执行第三步的话淘汰的应该是2),打印:[2, 4, 5]
      lruCacheDemo.put(5, 5);
      System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());
  }
}

that is all!!

这篇关于LRU算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!