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