当前位置: 首页 > 编程笔记 >

Java实现简单LRU缓存机制的方法

班展
2023-03-14
本文向大家介绍Java实现简单LRU缓存机制的方法,包括了Java实现简单LRU缓存机制的方法的使用技巧和注意事项,需要的朋友参考一下

一、什么是 LRU 算法

就是一种缓存淘汰策略。

计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置。但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用。

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

二、LRU的使用

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
    cache.put(1, 1);
    cache.put(2, 2);
    cache.get(1);    // 返回 1
    cache.put(3, 3);  // 该操作会使得密钥 2 作废

第一步:创建一个长度为2的LRUCache

第二步:cache.put(1, 1);放入key=1,value=1的数据

第三步:cache.put(2,2);放入key = 2,value = 2的数据
(因为2刚使用,所有把2移动到前面)

第四步:cache.get(1);获取key = 1的数据
(因为我们刚使用了1,所以把1移动到前面)

第五步:cache.put(3,3);放入key = 3,value = 3的数据
(因为3刚放进,所以放前面,又因为容量只有2,需要移除原先的1个。只因key = 2是最近最少使用的(key = 1刚get()过),所以移除2。

三、LRU的实现机制

算法:

LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。

1)双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。

2)哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。

一、初始化:

二、cache.put(1,1):

三、cache.put(2,2):

四、cache.get(1):

五、cache.put(3,3):

四、代码如下

import java.io.*;
import java.util.HashMap;

public class test {

  public static void main(String args[]) throws IOException {

    LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
    cache.put(1, 1);
    cache.put(2, 2);
    cache.get(1);    // 返回 1
    cache.put(3, 3);  // 该操作会使得密钥 2 作废
    cache.get(2);    // 返回 -1 (未找到)
    cache.put(4, 4);  // 该操作会使得密钥 1 作废
    cache.get(1);    // 返回 -1 (未找到)
    cache.get(3);    // 返回 3
    cache.get(4);    // 返回 4
  }
}

/**
 * 设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put
 */
class LRUCache {

  private HashMap<Integer,LinkedNode> cache = new HashMap();//方便通过key快速定位结点
  private int size;
  private int capacity;
  private LinkedNode head,tail;
  class LinkedNode{
    int key;
    int value;
    LinkedNode pre;
    LinkedNode next;
  }
  public LRUCache(int capacity) {
    this.size = 0;
    this.capacity = capacity;
    head = new LinkedNode();
    tail = new LinkedNode();
    head.next = tail;
    tail.pre = head;
  }

  /**
   * 移除节点
   * @param node
   */
  private void removeNode(LinkedNode node) {
    LinkedNode preNode = node.pre;
    LinkedNode nextNode = node.next;
    preNode.next = nextNode;
    nextNode.pre = preNode;
  }

  /**
   * 添加节点到头部
   * @param node
   */
  private void addNode(LinkedNode node) {
    node.pre = head;
    node.next = head.next;
    head.next.pre = node;
    head.next = node;
  }

  /**
   * 将节点移动到头部
   * @param node
   */
  private void moveToHead(LinkedNode node) {
    removeNode(node);
    addNode(node);
  }

  /**
   * 获取数据
   * @param key
   * @return
   */
  public int get(int key) {
    LinkedNode node = cache.get(key);
    if(node != null) {
      moveToHead(node);
    }else{
      return -1;
    }
    return node.value;
  }

  /**
   * 写入数据
   * @param key
   * @param value
   */
  public void put(int key, int value) {
    LinkedNode node = cache.get(key);
    //存在
    if(node != null) {
      node.value = value;//可能更新数据
      moveToHead(node);
    }
    //不存在
    else{
      LinkedNode newNode = new LinkedNode();
      newNode.key = key;
      newNode.value = value;
      cache.put(key,newNode);//更新Map
      addNode(newNode);//添加结点到头部
      size++;//更新结点数
      if(size > capacity) {//如果结点数超过容量大小
        LinkedNode tailPre = tail.pre;
        cache.remove(tailPre.key);//更新Map
        removeNode(tailPre);//删除最后一个结点(尾结点的前一个结点)
        size--;
      }
    }
  }
}

总结:自己实现的简单LRU总归太简单了,要是想完善或者实现更真实的LRU,不妨参考一下Redis中的LRU。(◔◡◔)

到此这篇关于Java实现简单LRU缓存机制的方法的文章就介绍到这了,更多相关Java LRU缓存内容请搜索小牛知识库以前的文章或继续浏览下面的相关文章希望大家以后多多支持小牛知识库!

 类似资料:
  • 问题内容: 我知道实现起来很简单,但是我想重用已经存在的东西。 我要解决的问题是,我为不同的页面,角色加载了配置(从XML,所以我想缓存它们),因此输入的组合可以增长很多(但99%的增长)。为了处理这一1%,我想在缓存中设置一些最大项目… 直到我在apache commons中找到了org.apache.commons.collections.map.LRUMap,它看起来还不错,但还想检查一下其

  • 本文向大家介绍详解Java实现LRU缓存,包括了详解Java实现LRU缓存的使用技巧和注意事项,需要的朋友参考一下 LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把

  • 本文向大家介绍Java实现LRU缓存的实例详解,包括了Java实现LRU缓存的实例详解的使用技巧和注意事项,需要的朋友参考一下 Java实现LRU缓存的实例详解 1.Cache Cache对于代码系统的加速与优化具有极大的作用,对于码农来说是一个很熟悉的概念。可以说,你在内存中new 了一个一段空间(比方说数组,list)存放一些冗余的结果数据,并利用这些数据完成了以空间换时间的优化目的,你就已经

  • 问题内容: 我试图使用LinkedHashMap实现LRU缓存。在LinkedHashMap的文档(http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html)中,它表示: 请注意,如果将密钥重新插入到映射中,则插入顺序不会受到影响。 但是当我做以下推 输出是 这表明重新插入确实影响了订单。有人知道任何解释吗? 问题答

  • 问题内容: 不要说EHCache或OSCache等。出于这个问题的目的,假设我想仅使用SDK实现自己的实现(边做边学)。考虑到缓存将在多线程环境中使用,你将使用哪些数据结构?我已经使用LinkedHashMap和Collections#synchronizedMap实现了一个,但是我很好奇是否有任何新的并发集合会更好。 更新:当我发现这个块时,我只是在阅读Yegge的最新文章: 如果你需要固定时间

  • 主要内容:1 LinkedHashMap的概述,2 LinkedHashMap的源码解析,2.1 主要类属性,2.2 构造器,2.4 常见API方法,2.5 大链表与迭代顺序的维护,3 LinkedHashMap与LRU缓存,3.1 afterNodeInsertion方法,3.2 removeEldestEntry方法,3.3 LRU缓存实现案例,4 LinkedHashMap的总结本文基于JDK1.8详细介绍了LinkedHashMap的底层原理,它到底是如何保证元素有序的?同时讲解了基于访