浅谈Java如何实现一个基于LRU时间复杂度为O(1)的缓存

 更新时间:2020年08月03日 16:23:18   作者:码农小麦  
这篇文章主要介绍了浅谈Java如何实现一个基于LRU时间复杂度为O(1)的缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

LRU:Least Recently Used最近最少使用,当缓存容量不足时,先淘汰最近最少使用的数据。就像JVM垃圾回收一样,希望将存活的对象移动到内存的一端,然后清除其余空间。

缓存基本操作就是读、写、淘汰删除。

读操作时间复杂度为O(1)的那就是hash操作了,可以使用HashMap索引 key。

写操作时间复杂度为O(1),使用链表结构,在链表的一端插入节点,是可以完成O(1)操作,但是为了配合读,还要再次将节点放入HashMap中,put操作最优是O(1),最差是O(n)。

不少童鞋就有疑问了,写入时又使用map进行了put操作,为何缓存不直接使用map?没错,首先使用map存储了节点数据就是采用空间换时间,但是淘汰删除不好处理,使用map如何去记录最近最少使用(涉及到时间、频次问题)。so,使用链表可以将活跃节点移动到链表的一端,淘汰时直接从另一端进行删除。

public class LruCache<K,V> {
	/** 这里简单点直接初始化了*/
  private int capacity = 2;
  private int size = 0;
  private HashMap<K,DoubleListNode<K,V>> cache = new HashMap<>(capacity);
  private DoubleListNode<K,V> lruNode = new DoubleListNode<K, V>(null,null,null,null);
  private DoubleListNode<K,V> mruNode = new DoubleListNode<K, V>(null,null,null,null);

  public V get(K key){
    DoubleListNode<K,V> target = cache.get(key);
    if (target == null) {
      return null;
    }
    /** 使用过就移动到右侧 */
    move2mru(target);
    return target.value;
  }

  public void put(K key,V value){
    if(cache.containsKey(key)){
      DoubleListNode<K,V> temp = cache.get(key);
      temp.value = value;
      /** 使用过就移动到右侧 */
      move2mru(temp);
      return;
    }

		/** 容量满了清除左侧 */
    if(size >= capacity){
      evict4lru();
    }
    DoubleListNode<K,V> newNode = new DoubleListNode<>(mruNode,null,key,value);
    if(size == 0){
      lruNode.next = newNode;
    }
    mruNode.next = newNode;
    mruNode = newNode;
    cache.put(key,newNode);
    size++;
  }

  private void move2mru(DoubleListNode<K,V> newMru){
    DoubleListNode<K,V> pre = newMru.pre;
    DoubleListNode<K,V> next = newMru.next;
    pre.next = next;
    newMru.pre = mruNode;
    mruNode.next = newMru;
    mruNode = newMru;
  }

  private void evict4lru(){
  	cache.remove(lruNode.next.key);
    lruNode.next = lruNode.next.next;
    size--;
  }

  public String toString(){
    StringBuffer sb = new StringBuffer("lru -> ");
    DoubleListNode<K,V> temp = lruNode;
    while(temp!=null){
      sb.append(temp.key).append(":").append(temp.value);
      sb.append(" -> ");
      temp = temp.next;
    }
    sb.append(" -> mru ");
    return sb.toString();
  }

  public static void main(String[] args) {
    LruCache<String,String> cache = new LruCache<>();
    cache.put("1","1");
    System.out.println(cache);
    cache.get("1");
    cache.put("2","2");
    System.out.println(cache);
    cache.put("3","3");
    System.out.println(cache);
    cache.put("4","4");
    System.out.println(cache);
  }
}

class DoubleListNode<K,V>{
  K key;
  V value;
  DoubleListNode<K,V> pre;
  DoubleListNode<K,V> next;

  public DoubleListNode(K key,V value){
    this.key = key;
    this.value = value;
  }

  public DoubleListNode(DoubleListNode<K,V> pre,DoubleListNode<K,V> next,K key,V value){
    this.pre = pre;
    this.next = next;
    this.key = key;
    this.value = value;
  }
}

这里使用链表,及HashMap完成了基于LRU的缓存,其中HashMap主要用来快速索引key,链表用来完成LRU机制。当然尚有许多不足,包括缓存移除remove,缓存ttl,线程安全等。

到此这篇关于浅谈Java如何实现一个基于LRU时间复杂度为O(1)的缓存的文章就介绍到这了,更多相关Java基于LRU时间复杂度为O(1)的缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用IDEA和Gradle构建Vertx项目(图文步骤)

    使用IDEA和Gradle构建Vertx项目(图文步骤)

    这篇文章主要介绍了使用IDEA和Gradle构建Vertx项目(图文步骤),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • Java实现爬虫

    Java实现爬虫

    这篇文章介绍了Java实现爬虫的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-01-01
  • 如何解决通过spring-boot-maven-plugin package失败问题

    如何解决通过spring-boot-maven-plugin package失败问题

    这篇文章主要介绍了如何解决通过spring-boot-maven-plugin package失败问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • RestTemplate集成Ribbbon的示例代码

    RestTemplate集成Ribbbon的示例代码

    这篇文章主要介绍了RestTemplate集成Ribbbon的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Java连接mysql数据库代码实例程序

    Java连接mysql数据库代码实例程序

    这篇文章主要介绍了java连接mysql数据库代码实例程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java调用Shell命令和脚本的实现

    Java调用Shell命令和脚本的实现

    这篇文章主要介绍了Java调用Shell命令和脚本的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • SpringBoot 定时任务遇到的坑

    SpringBoot 定时任务遇到的坑

    这篇文章主要介绍了SpringBoot 定时任务遇到的坑,今天踩的这个坑和 cron 表达式有关,文中给大家介绍了cron 表达式的解释,需要的朋友一起看看吧
    2017-11-11
  • Maven中两个命令clean 和 install的使用

    Maven中两个命令clean 和 install的使用

    Maven是一个项目管理和自动构建工具,clean命令用于删除项目中由先前构建生成的target目录,install命令用于将打包好的jar包安装到本地仓库中,供其他项目依赖使用,下面就来详细的介绍一下这两个命令
    2024-09-09
  • Java8新特性Stream短路终端操作实例解析

    Java8新特性Stream短路终端操作实例解析

    这篇文章主要介绍了Java8新特性Stream短路终端操作实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • java两个integer数据判断相等用==还是equals

    java两个integer数据判断相等用==还是equals

    本文主要介绍了java两个integer数据判断相等用==还是equals,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12

最新评论