- public class LruCache < K,
- V > {
- // 定义一个 LinkedHashMap
- private final LinkedHashMap < K,
- V > map;
- private int size; // 初始大小
- private int maxSize; // 最大容量
- private int putCount; // 插入个数
- private int createCount; // 创建个数
- private int evictionCount; // 回收个数
- private int hitCount; // 找到 key 的个数
- private int missCount; // 没找到 key 的个数
- // 构造函数, 传递进来一个最大容量值
- 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);
- }
- // 设置 cache 的大小
- public void resize(int maxSize) {
- if (maxSize <= 0) {
- throw new IllegalArgumentException("maxSize <= 0");
- }
- synchronized(this) {
- this.maxSize = maxSize;
- }
- trimToSize(maxSize);
- }
- // 如果通过 key 查找到 value 存在于 cache 中就直接返回或者通过 create 方法创建一个然后返回
- // 如果这个值被返回了, 那么它将移动到队列的头部
- // 如果一个值没有被缓存同时也不能被创建则返回 null
- public final V get(K key) {
- if (key == null) {
- throw new NullPointerException("key == null");
- }
- V mapValue;
- synchronized(this) {
- mapValue = map.get(key);
- // 找到对应值, 命中 + 1, 直接返回该值
- if (mapValue != null) {
- hitCount++;
- return mapValue;
- }
- // 否则未命中 + 1
- missCount++;
- }
- // 如果没找到 key 对应的值那么就尝试创建一个, 也许花费较长的时间
- // 并且创建后返回的 map 也许和之前的不同, 如果创建的值和 map 中有冲突的话
- // 那么我们就释放掉创建的值, 保留 map 中的值
- V createdValue = create(key);
- // 通过观察后面的 create() 方法, 可以看到直接 return null;
- // 那么我们需要想一想为什么源码中是直接返回 null 呢?
- // 因为 LruCache 常常作为内存缓存而存在, 所以当我们查找 key 找不到对应的 value 时
- // 这个时候我们应该从其他方面, 比如文件缓存或者网络中请求数据
- // 而不是我们随便赋值创建一个值返回, 所以这里返回 null 是合理的
- // 如果自己真的有需要的话, 自己需要重写 create 方法, 手动创建一个值返回
- if (createdValue == null) {
- return null;
- }
- // 走到这儿说明创建了一个不为 null 的值
- synchronized(this) {
- createCount++; // 创建个数 + 1
- // 把创建的 value 插入到 map 对应的 key 中
- // 并且将原来键为 key 的对象保存到 mapValue
- mapValue = map.put(key, createdValue);
- if (mapValue != null) {
- // 如果 mapValue 不为空, 说明原来 key 对应的是有值的, 则撤销上一步的 put 操作
- map.put(key, mapValue);
- } else {
- // 加入新创建的对象之后需要重新计算 size 大小
- size += safeSizeOf(key, createdValue);
- }
- }
- if (mapValue != null) {
- entryRemoved(false, key, createdValue, mapValue);
- return mapValue;
- } else {
- // 每次新加入对象都需要调用 trimToSize 方法看是否需要回收
- trimToSize(maxSize);
- return createdValue;
- }
- }
- // 将 key 对应的 value 缓存起来, 放在队列的头部
- // 返回 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++; // 插入数量 + 1
- size += safeSizeOf(key, value); // 重新计算大小
- // 得到 key 对应的前一个 value, 如果之前无值, 返回 null, 如果有值, 返回前一个值
- previous = map.put(key, value);
- if (previous != null) {
- size -= safeSizeOf(key, previous);
- }
- }
- if (previous != null) {
- entryRemoved(false, key, previous, value);
- }
- trimToSize(maxSize);
- // 返回之前 key 对应的旧值 value
- return previous;
- }
- // 根据 maxSize 来调整内存 cache 的大小, 如果 maxSize 传入 - 1, 则清空缓存中的所有对象
- public 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!");
- }
- // 如果当前 size 小于 maxSize 或者 map 没有任何对象, 则结束循环
- 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++; // 回收个数 + 1
- }
- entryRemoved(true, key, value, null);
- }
- }
- // 从内存缓存中根据 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.
- *
- * <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}.
- * @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.
- *
- * <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.
- *
- * <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.
- */
- // 回收所有对象
- 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.
- */
- 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);
- }
- }
来源: http://blog.csdn.net/woshizisezise/article/details/79347260