LruCache - 一天一点源代码

LruCache - 一天一点源码

说明


/**
 * 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.
 *
 * <p>If your cached values hold resources that need to be explicitly released,
 * override {@link #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.
 *
 * <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:
 * <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.
 */

1、前言

一个高速缓存对有限数量的值持有强引用。每当一个值被访问时,该值被移动到队列头部。当一个值被添加到存满的缓存队列中时,在缓存数据中位于缓存队列最后的数据会被移出或者被回收。

2、需要重写的方法

如果缓存值持有的资源需要被释放,需要重写entryRemoved方法

protected void entryRemoved(boolean evi cted, K key, V oldValue, V newValue) {}

如果丢失了一个缓存值,需要根据需求对其对应的键进行计算,需要重写create方法,这样简化了代码的调用,当丢失一个缓存值,仍然能通过计算返回该值,使得总有一个值被返回。

protected V create(K key) {
        return null;
    }

默认情况下,缓存空间根据条目的数量而定,重写sizeOf方法,根据需求,设置返回不同的缓存空间。


protected int sizeOf(K key, V value) {
        return 1;
    }

此类不允许使用null作为key或者value,以下 get,put,remove方法在key值为空的情况下允许返回null。

3、方法

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);
}

可以看出,在创建LruCache实例的时候需要传入一个maxSize
在实例化过程中,将传入的maxSize的值赋给全局的maxSize,并创建了map的LinkedHashMap对象。由此可见,LruCache是通过LinkedHashMap进行数据存储的。

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

通过sizOf获取放入的数据需要的空间大小。

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++;
        }

在get方法中,传入了键的值,返回了value值,内部通过map中的get(key)取值,由于V为map的一个泛型,返回时,也是返回get方法中定义的mapValue,所以是存取数据变得方便。

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;
    }

在remove方法中,传入了键的值,返回了value值,先通过map中的remove(key)移除。若失败,再通过重写的entryRemoved方法将缓存值持有的资源进行释放。

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);
        }
    }

内部是一个无限循环,新数据空间不足以放入时,自动删除最近最少访问的键值对。

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) {
                size -= safeSizeOf(key, previous);
            }
        }

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

        trimToSize(maxSize);
        return previous;
}

键、值均不能为空。放入一个数据,并且调用了trimToSize对缓存中的数据进行检测和清理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值