Java资源缓存 之 LruCache

例如对 网络加载图片进行缓存 :

  // 得到 应用程序 被分配的最大的内存
    int maxMemory=(int) Runtime.getRuntime().maxMemory();
    // 取处内存的 1/5 用来当 缓存 大小
    int cachSize=maxMemory/5;
    // 实例化 LruCache
    lruCache=new lruCache<String, Bitmap>(cachSize){
      //内部方法sizeOf设置每一张图片的缓存大小
      protected int sizeOf(String key, Bitmap value) {
        //在每次存入缓存时调用,告诉系统这张缓存图片有多大
        // 相当于 为每次 要缓存的 资源 分配 大小空间
        return value.getByteCount();
      }
    };

上面的 代码 一般 放在初始化的 方法 里面

其实 可以将 LurCache 类 理解 为 Map 类 map 有 put和 get 方法

接下去就调用put 和 get 方法 进行需要缓存资源的存取

LurCache 的 add :

public void putBitmapToCache(String url,Bitmap bitmap){
    if (getBitmapfromCache(url)==null) {//判断当前缓存是否存在
      lruCache.put(url, bitmap);
    }
  }
LurCache 的 get:

public Bitmap getBitmapfromCache(String url){
    return lruCache.get(url);//可将lruCache看成map
  }

调用上面的 add 和 get 方法 就可以对资源进行缓存的 ,还是挺简单的,

但要注意一点 LruCache lruCache=new LruCache<String, Bitmap>(cachSize) 只能被new 一次 ,不然不同对象就不同的缓存了

附上Android的Lrucache类

package android.util; 

import java.util.LinkedHashMap;
import java.util.Map; 

/**
 * A cache that holds strong references to a limited number of values. Each time
 * a value is accessed, it is moved to the head of a queue. When a value is
 * added to a full cache, the value at the end of that queue is evicted and may
 * become eligible for garbage collection.
 * Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。
 * 当cache已满的时候加入新的item时,在队列尾部的item会被回收。
 * <p>If your cached values hold resources that need to be explicitly released,
 * override {@link #entryRemoved}.
 * 如果你cache的某个值需要明确释放,重写entryRemoved()
 * <p>If a cache miss should be computed on demand for the corresponding keys,
 * override {@link #create}. This simplifies the calling code, allowing it to
 * assume a value will always be returned, even when there's a cache miss.
 * 如果key相对应的item丢掉啦,重写create().这简化了调用代码,即使丢失了也总会返回。
 * <p>By default, the cache size is measured in the number of entries. Override
 * {@link #sizeOf} to size the cache in different units. For example, this cache
 * is limited to 4MiB of bitmaps: 默认cache大小是测量的item的数量,重写sizeof计算不同item的
 * 大小。
 * <pre>  {@code
 *  int cacheSize = 4 * 1024 * 1024; // 4MiB
 *  LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
 *    protected int sizeOf(String key, Bitmap value) {
 *      return value.getByteCount();
 *    }
 *  }}</pre>
 *
 * <p>This class is thread-safe. Perform multiple cache operations atomically by
 * synchronizing on the cache: <pre>  {@code
 *  synchronized (cache) {
 *   if (cache.get(key) == null) {
 *     cache.put(key, value);
 *   }
 *  }}</pre>
 *
 * <p>This class does not allow null to be used as a key or value. A return
 * value of null from {@link #get}, {@link #put} or {@link #remove} is
 * unambiguous: the key was not in the cache.
 * 不允许key或者value为null
 * 当get(),put(),remove()返回值为null时,key相应的项不在cache中
 */
public class LruCache<K, V> {
  private final LinkedHashMap<K, V> map; 

  /** Size of this cache in units. Not necessarily the number of elements. */
  private int size; //已经存储的大小
  private int maxSize; //规定的最大存储空间

  private int putCount; //put的次数
  private int createCount; //create的次数
  private int evictionCount; //回收的次数
  private int hitCount; //命中的次数
  private int missCount; //丢失的次数

  /**
   * @param maxSize for caches that do not override {@link #sizeOf}, this is
   *   the maximum number of entries in the cache. For all other caches,
   *   this is the maximum sum of the sizes of the entries in this cache.
   */
  public LruCache(int maxSize) {
    if (maxSize <= 0) {
      throw new IllegalArgumentException("maxSize <= 0");
    }
    this.maxSize = maxSize;
    this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
  } 

  /**
   * Returns the value for {@code key} if it exists in the cache or can be
   * created by {@code #create}. If a value was returned, it is moved to the
   * head of the queue. This returns null if a value is not cached and cannot
   * be created. 通过key返回相应的item,或者创建返回相应的item。相应的item会移动到队列的头部,
   * 如果item的value没有被cache或者不能被创建,则返回null。
   */
  public final V get(K key) {
    if (key == null) {
      throw new NullPointerException("key == null");
    } 

    V mapValue;
    synchronized (this) {
      mapValue = map.get(key);
      if (mapValue != null) {
        hitCount++; //命中
        return mapValue;
      }
      missCount++; //丢失
    } 

    /*
     * Attempt to create a value. This may take a long time, and the map
     * may be different when create() returns. If a conflicting value was
     * added to the map while create() was working, we leave that value in
     * the map and release the created value.
     * 如果丢失了就试图创建一个item
     */ 

    V createdValue = create(key);
    if (createdValue == null) {
      return null;
    } 

    synchronized (this) {
      createCount++;//创建++
      mapValue = map.put(key, createdValue); 

      if (mapValue != null) {
        // There was a conflict so undo that last put
        //如果前面存在oldValue,那么撤销put()
        map.put(key, mapValue);
      } else {
        size += safeSizeOf(key, createdValue);
      }
    } 

    if (mapValue != null) {
      entryRemoved(false, key, createdValue, mapValue);
      return mapValue;
    } else {
      trimToSize(maxSize);
      return createdValue;
    }
  } 

  /**
   * Caches {@code value} for {@code key}. The value is moved to the head of
   * the queue.
   *
   * @return the previous value mapped by {@code key}.
   */
  public final V put(K key, V value) {
    if (key == null || value == null) {
      throw new NullPointerException("key == null || value == null");
    } 

    V previous;
    synchronized (this) {
      putCount++;
      size += safeSizeOf(key, value);
      previous = map.put(key, value);
      if (previous != null) { //返回的先前的value值
        size -= safeSizeOf(key, previous);
      }
    } 

    if (previous != null) {
      entryRemoved(false, key, previous, value);
    } 

    trimToSize(maxSize);
    return previous;
  } 

  /**
   * @param maxSize the maximum size of the cache before returning. May be -1
   *   to evict even 0-sized elements.
   * 清空cache空间
   */
  private void trimToSize(int maxSize) {
    while (true) {
      K key;
      V value;
      synchronized (this) {
        if (size < 0 || (map.isEmpty() && size != 0)) {
          throw new IllegalStateException(getClass().getName()
              + ".sizeOf() is reporting inconsistent results!");
        } 

        if (size <= maxSize) {
          break;
        } 

        Map.Entry<K, V> toEvict = map.eldest();
        if (toEvict == null) {
          break;
        } 

        key = toEvict.getKey();
        value = toEvict.getValue();
        map.remove(key);
        size -= safeSizeOf(key, value);
        evictionCount++;
      } 

      entryRemoved(true, key, value, null);
    }
  } 

  /**
   * Removes the entry for {@code key} if it exists.
   * 删除key相应的cache项,返回相应的value
   * @return the previous value mapped by {@code key}.
   */
  public final V remove(K key) {
    if (key == null) {
      throw new NullPointerException("key == null");
    } 

    V previous;
    synchronized (this) {
      previous = map.remove(key);
      if (previous != null) {
        size -= safeSizeOf(key, previous);
      }
    } 

    if (previous != null) {
      entryRemoved(false, key, previous, null);
    } 

    return previous;
  } 

  /**
   * Called for entries that have been evicted or removed. This method is
   * invoked when a value is evicted to make space, removed by a call to
   * {@link #remove}, or replaced by a call to {@link #put}. The default
   * implementation does nothing.
   * 当item被回收或者删掉时调用。改方法当value被回收释放存储空间时被remove调用,
   * 或者替换item值时put调用,默认实现什么都没做。
   * <p>The method is called without synchronization: other threads may
   * access the cache while this method is executing.
   *
   * @param evicted true if the entry is being removed to make space, false
   *   if the removal was caused by a {@link #put} or {@link #remove}.
   * true---为释放空间被删除;false---put或remove导致
   * @param newValue the new value for {@code key}, if it exists. If non-null,
   *   this removal was caused by a {@link #put}. Otherwise it was caused by
   *   an eviction or a {@link #remove}.
   */
  protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {} 

  /**
   * Called after a cache miss to compute a value for the corresponding key.
   * Returns the computed value or null if no value can be computed. The
   * default implementation returns null.
   * 当某Item丢失时会调用到,返回计算的相应的value或者null
   * <p>The method is called without synchronization: other threads may
   * access the cache while this method is executing.
   *
   * <p>If a value for {@code key} exists in the cache when this method
   * returns, the created value will be released with {@link #entryRemoved}
   * and discarded. This can occur when multiple threads request the same key
   * at the same time (causing multiple values to be created), or when one
   * thread calls {@link #put} while another is creating a value for the same
   * key.
   */
  protected V create(K key) {
    return null;
  } 

  private int safeSizeOf(K key, V value) {
    int result = sizeOf(key, value);
    if (result < 0) {
      throw new IllegalStateException("Negative size: " + key + "=" + value);
    }
    return result;
  } 

  /**
   * Returns the size of the entry for {@code key} and {@code value} in
   * user-defined units. The default implementation returns 1 so that size
   * is the number of entries and max size is the maximum number of entries.
   * 返回用户定义的item的大小,默认返回1代表item的数量,最大size就是最大item值
   * <p>An entry's size must not change while it is in the cache.
   */
  protected int sizeOf(K key, V value) {
    return 1;
  } 

  /**
   * Clear the cache, calling {@link #entryRemoved} on each removed entry.
   * 清空cacke
   */
  public final void evictAll() {
    trimToSize(-1); // -1 will evict 0-sized elements
  } 

  /**
   * For caches that do not override {@link #sizeOf}, this returns the number
   * of entries in the cache. For all other caches, this returns the sum of
   * the sizes of the entries in this cache.
   */
  public synchronized final int size() {
    return size;
  } 

  /**
   * For caches that do not override {@link #sizeOf}, this returns the maximum
   * number of entries in the cache. For all other caches, this returns the
   * maximum sum of the sizes of the entries in this cache.
   */
  public synchronized final int maxSize() {
    return maxSize;
  } 

  /**
   * Returns the number of times {@link #get} returned a value that was
   * already present in the cache.
   */
  public synchronized final int hitCount() {
    return hitCount;
  } 

  /**
   * Returns the number of times {@link #get} returned null or required a new
   * value to be created.
   */
  public synchronized final int missCount() {
    return missCount;
  } 

  /**
   * Returns the number of times {@link #create(Object)} returned a value.
   */
  public synchronized final int createCount() {
    return createCount;
  } 

  /**
   * Returns the number of times {@link #put} was called.
   */
  public synchronized final int putCount() {
    return putCount;
  } 

  /**
   * Returns the number of values that have been evicted.
   * 返回被回收的数量
   */
  public synchronized final int evictionCount() {
    return evictionCount;
  } 

  /**
   * Returns a copy of the current contents of the cache, ordered from least
   * recently accessed to most recently accessed. 返回当前cache的副本,从最近最少访问到最多访问
   */
  public synchronized final Map<K, V> snapshot() {
    return new LinkedHashMap<K, V>(map);
  } 

  @Override public synchronized final String toString() {
    int accesses = hitCount + missCount;
    int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
    return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
        maxSize, hitCount, missCount, hitPercent);
  }
}
(0)

相关推荐

  • 详解Java实现缓存(LRU,FIFO)

    现在软件或者网页的并发量越来越大了,大量请求直接操作数据库会对数据库造成很大的压力,处理大量连接和请求就会需要很长时间,但是实际中百分之80的数据是很少更改的,这样就可以引入缓存来进行读取,减少数据库的压力. 常用的缓存有Redis和memcached,但是有时候一些小场景就可以直接使用Java实现缓存,就可以满足这部分服务的需求. 缓存主要有LRU和FIFO,LRU是Least Recently Used的缩写,即最近最久未使用,FIFO就是先进先出,下面就使用Java来实现这两种缓存. LR

  • Java和Android的LRU缓存及实现原理

    一.概述 Android提供了LRUCache类,可以方便的使用它来实现LRU算法的缓存.Java提供了LinkedHashMap,可以用该类很方便的实现LRU算法,Java的LRULinkedHashMap就是直接继承了LinkedHashMap,进行了极少的改动后就可以实现LRU算法. 二.Java的LRU算法 Java的LRU算法的基础是LinkedHashMap,LinkedHashMap继承了HashMap,并且在HashMap的基础上进行了一定的改动,以实现LRU算法. 1.Hash

  • java LRU(Least Recently Used )详解及实例代码

    java LRU(Least Recently Used )详解 LRU是Least Recently Used 的缩写,翻译过来就是"最近最少使用",LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据

  • Java资源缓存 之 LruCache

    例如对 网络加载图片进行缓存 : // 得到 应用程序 被分配的最大的内存 int maxMemory=(int) Runtime.getRuntime().maxMemory(); // 取处内存的 1/5 用来当 缓存 大小 int cachSize=maxMemory/5; // 实例化 LruCache lruCache=new lruCache<String, Bitmap>(cachSize){ //内部方法sizeOf设置每一张图片的缓存大小 protected int size

  • 详解Java分布式缓存系统中必须解决的四大问题

    目录 缓存穿透 缓存击穿 缓存雪崩 缓存一致性 分布式缓存系统是三高架构中不可或缺的部分,极大地提高了整个项目的并发量.响应速度,但它也带来了新的需要解决的问题,分别是: 缓存穿透.缓存击穿.缓存雪崩和缓存一致性问题. 缓存穿透 第一个比较大的问题就是缓存穿透.这个概念比较好理解,和命中率有关.如果命中率很低,那么压力就会集中在数据库持久层. 假如能找到相关数据,我们就可以把它缓存起来.但问题是,本次请求,在缓存和持久层都没有命中,这种情况就叫缓存的穿透. 举个例子,如上图,在一个登录系统中,有

  • Android缓存机制——LruCache的详解

    概述 LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量,值得注意的4个方法:构造方法.get.put.trimToSize LRU(Least Recently Used)缓存算法便应运而生,LRU是最近最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些最近最少使用的缓存对象.采用LRU算法的缓存有两种:LrhCache和DisLruCache,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法. LRU原理

  • 如何基于LoadingCache实现Java本地缓存

    这篇文章主要介绍了如何基于LoadingCache实现Java本地缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 Guava是Google开源出来的一套工具库.其中提供的cache模块非常方便,是一种与ConcurrentMap相似的缓存Map. 官方地址:https://github.com/google/guava/wiki/CachesExplained 开始构建 一. 添加依赖 <dependency> <groupI

  • Java内存缓存工具Guava LoadingCache使用解析

    这篇文章主要介绍了Java内存缓存工具Guava LoadingCache使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.Guava介绍 Guava是Google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中.实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问. Guava Cache是单个应用运行时的本地缓存.它不把数据存放到文件或外部服务器.如果不符合需求,可以选择Memcached.Redis等工具

  • webpack实现静态资源缓存的方法

    目录 引言 区分一下几种不同的hash hash chunkhash contenthash 实现js缓存 CommonsChunkPlugin不正确用法 引起问题的原因 实现css的缓存 实现图片/字体的缓存 参考 引言 静态资源缓存是前端性能优化的一个点,所以在前端开发过程中,一般会最大限度的利用缓存(这里主要是强缓存).回到本文主题,在使用webpack构建的项目中,稍有不慎的话,即使服务器设置了缓存策略,可能构建的项目无法实现静态资源缓存.那么webpack怎样才能达到使用缓存的效果呢,

  • Java本地缓存工具之LoadingCache的使用详解

    目录 前言 环境依赖 代码 演示一下 总结 前言 在工作总常常需要用到缓存,而redis往往是首选,但是短期的数据缓存一般我们还是会用到本地缓存.本文提供一个我在工作中用到的缓存工具,该工具代码为了演示做了一些调整.如果拿去使用的话,可以考虑做成注入Bean对象,看具体需求了. 环境依赖 先添加maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</arti

  • Java字节缓存流的构造方法之文件IO流

    目录 1.字节缓冲流 1.1字节缓冲流构造方法 1.2字节流复制视频 2.字符流 2.1为什么会出现字符流 2.2编码表 2.3字符串中的编码解码问题 2.4字符流中的编码解码问题 2.5字符流写数据的5种方式 2.6字符流读数据的2种方式 2.7字符流复制Java文件 2.8字符流复制Java文件改进版 2.9字符缓冲流 2.10字符缓冲流复制Java文件 2.11字符缓冲流特有功能 2.12字符缓冲流特有功能复制Java文件 2.13IO流小结 3练习案例 3.1集合到文件 3.2文件到集合

  • 基于Java实现缓存Cache的深入分析

    原理是使用LinkedHashMap来实现,当缓存超过大小时,将会删除最老的一个元组.实现代码如下所示 复制代码 代码如下: import java.util.LinkedHashMap;import java.util.Map;public class LRUCache { public static class CachedData {  private Object data = null;  private long time = 0;  private boolean refreshi

随机推荐