【Java源码分析】Android-LruCache源码分析

深藏阁楼爱情的钟 2022-07-20 12:05 333阅读 0赞

内部实现是LinkedHashMap,保持有限数量的值得强引用,值被访问之后就被移动到队列的首部。当队列满了之后,尾部的值会被移除以便于GC回收

类的定义

  1. public class LruCache<K, V> {}
  1. 如果被缓存的值所拥有的职员需要被显式的释放,重载entryRemoved()方法
  2. 默认情况下size方法返回的是条目数,如果需要返回缓存大小,可以重载sizeOf()
  3. 线程安全的类
  4. 不允许NULL的key,因此get put remove返回null的时候是没有二义性的

构造方法

  1. public LruCache(int maxSize) {
  2. if (maxSize <= 0) {
  3. throw new IllegalArgumentException("maxSize <= 0");
  4. }
  5. this.maxSize = maxSize;
  6. this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
  7. }

创建一个LruCache,如果没有重载sizeOf()那么maxSize是最大条目数,如果重载了,就是用于重载所用单位

取值方法

  1. public final V get(K key) {
  2. if (key == null) {
  3. throw new NullPointerException("key == null");
  4. }
  5. V mapValue;
  6. synchronized (this) {
  7. mapValue = map.get(key);
  8. if (mapValue != null) {
  9. hitCount++;
  10. return mapValue;
  11. }
  12. missCount++;
  13. }
  14. /*
  15. * Attempt to create a value. This may take a long time, and the map
  16. * may be different when create() returns. If a conflicting value was
  17. * added to the map while create() was working, we leave that value in
  18. * the map and release the created value.
  19. */
  20. V createdValue = create(key);
  21. if (createdValue == null) {
  22. return null;
  23. }
  24. synchronized (this) {
  25. createCount++;
  26. mapValue = map.put(key, createdValue);
  27. if (mapValue != null) {
  28. // There was a conflict so undo that last put
  29. map.put(key, mapValue);
  30. } else {
  31. size += safeSizeOf(key, createdValue);
  32. }
  33. }
  34. if (mapValue != null) {
  35. entryRemoved(false, key, createdValue, mapValue);
  36. return mapValue;
  37. } else {
  38. trimToSize(maxSize);
  39. return createdValue;
  40. }
  41. }

如果对应的key存在于缓存或者可以被创建,那么返回key对应的value。否则返回null.
如果在创建过程中出现了值得冲突,那么就丢弃当前创建的value,保留之前的。

存值方法

  1. public final V put(K key, V value) {
  2. if (key == null || value == null) {
  3. throw new NullPointerException("key == null || value == null");
  4. }
  5. V previous;
  6. synchronized (this) {
  7. putCount++;
  8. size += safeSizeOf(key, value);
  9. previous = map.put(key, value);
  10. if (previous != null) {
  11. size -= safeSizeOf(key, previous);
  12. }
  13. }
  14. if (previous != null) {
  15. entryRemoved(false, key, previous, value);
  16. }
  17. trimToSize(maxSize);
  18. return previous;
  19. }

将当前的键值对存入Map,并把当前键值对移到队列最前方,返回该key之前映射的值

  1. protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

键值对被移除时调用,默认是空实现。方法是没有实现同步的,所以在执行时可能被其他线程访问。如果第一个参数是true,就代表是移除以空出空间。如果是false代表是被put或者remove调用。最后一个参数是key对应的新值,如果非空,就说明是被put调用,如果为空,说明是被eviction或者remove

移除最久的键值对

  1. public void trimToSize(int maxSize) {
  2. while (true) {
  3. K key;
  4. V value;
  5. synchronized (this) {
  6. if (size < 0 || (map.isEmpty() && size != 0)) {
  7. throw new IllegalStateException(getClass().getName()
  8. + ".sizeOf() is reporting inconsistent results!");
  9. }
  10. if (size <= maxSize) {
  11. break;
  12. }
  13. Map.Entry<K, V> toEvict = map.eldest();
  14. if (toEvict == null) {
  15. break;
  16. }
  17. key = toEvict.getKey();
  18. value = toEvict.getValue();
  19. map.remove(key);
  20. size -= safeSizeOf(key, value);
  21. evictionCount++;
  22. }
  23. entryRemoved(true, key, value, null);
  24. }
  25. }

移除存在最久的键值对,保证缓存容量在指定容量之内,参数就是缓存阈值。

删除操作

  1. public final V remove(K key) {
  2. if (key == null) {
  3. throw new NullPointerException("key == null");
  4. }
  5. V previous;
  6. synchronized (this) {
  7. previous = map.remove(key);
  8. if (previous != null) {
  9. size -= safeSizeOf(key, previous);
  10. }
  11. }
  12. if (previous != null) {
  13. entryRemoved(false, key, previous, null);
  14. }
  15. return previous;
  16. }

删除key所映射的之前的值,返回值是key先前的映射值

缓存未命中时计算给定key的相关值时被调用的方法,在get中被使用(当get的时候当前key没有对应的键值对,就需要调用create()创建)这个方法需要和get结合来看

  1. protected V create(K key) {
  2. return null;
  3. }

求大小

  1. private int safeSizeOf(K key, V value) {
  2. int result = sizeOf(key, value);
  3. if (result < 0) {
  4. throw new IllegalStateException("Negative size: " + key + "=" + value);
  5. }
  6. return result;
  7. }
  8. protected int sizeOf(K key, V value) {
  9. return 1;
  10. }

返回给定参数键值对应实体的大小,该大小是用户定义的单位大小比如个数或者KB。默认返回1代表个数

清空缓存

  1. public final void evictAll() {
  2. trimToSize(-1); // -1 will evict 0-sized elements
  3. }

对每一个entry调用entryRemoved

获取缓存大小

  1. public synchronized final int size() {
  2. return size;
  3. }

对于没有覆写sizeOf()的时候返回的是缓存中实体的最大个数,如果覆写了sizeOf()返回的是所有实体大小之和的最大值

缓存命中数

  1. public synchronized final int hitCount() {
  2. return hitCount;
  3. }
  4. public synchronized final int missCount() {
  5. return missCount;
  6. }

get方法命中次数和未命中数

返回快照

  1. // ordered from least recently accessed to most recently accessed.
  2. public synchronized final Map<K, V> snapshot() {
  3. return new LinkedHashMap<K, V>(map);
  4. }

返回当前缓存的一个副本

发表评论

表情:
评论列表 (有 0 条评论,333人围观)

还没有评论,来说两句吧...

相关阅读