LRU 咋一看这么熟悉,操作系统里面内存管理,页面置换时替换算法之一,英文全拼为Least Recently Used 以为最近最少使用,简单来说,就是替换掉最老的数据。其核心思想为如果数据最近被访问过,那么将来被访问的几率也更高。另外一个比较简单的算法是 FIFO,First In First Out 先进先出,就是淘汰最先使用的,也就是说留下最近使用的,看似这两个很像,实际上区别还是很大的,当缓存命中时,LRU会将元素移到队首,而FIFO不会变 我觉得这是对于 LRU 和 FIFO 比较正确的定义,就实现方式来说,FIFO 相对简单些
前世今生
1 2
// 很干净,没有父类(Object 除外) public class LruCache<K, V> {}
/** Size of this cache in units. Not necessarily the number of elements. */ private int size; // 阈值设置,超过阈值就会抛弃 private int maxSize; /** * 下面的统计数据主要是用来评估的 */ // 调用 put 的次数 private int putCount; // 创建 key 的次数 private int createCount; // 淘汰数据 的次数 private int evictionCount; // 命中的次数 private int hitCount; // 未命中的次数 private int missCount;
/** * 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) { // 注意不允许为空,HashMap 是可以允许你 Key Value 为空的 if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); }
/* * 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. */ // 缓存失效 // 或者不存在key,同样 create 也是空方法,使用者根据自己需求去实现 V createdValue = create(key); if (createdValue == null) { return null; } // 就是一个 put 操作 synchronized (this) { createCount++; mapValue = map.put(key, createdValue);
if (mapValue != null) { // There was a conflict so undo that last put map.put(key, mapValue); } else { size += safeSizeOf(key, createdValue); } }
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. */ // 子类可重写,默认返回1,根据使用情况自己计算 // 扯一句,关于 Java 对象在内存的大小如何计算,请自行去查找 protected int sizeOf(K key, V value) { return 1; }
总结
HashMap 是个很重要的东西,下一次我们在慢慢分析
今天跟大家分享了一个很简单的数据结构,也是你可以效仿的对象,还是那句,Read the fucking source code