先看几个构造方法
/**
* 双向链表的头部
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* 双向链表的尾端
*/
transient LinkedHashMap.Entry<K,V> tail;
public LinkedHashMap() {
super();
//accessOrder为false则表示按插入顺序排序
//accessOrder为true则表示按访问顺序排序
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
/**
* 重写父类的newNode方法
*/
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
/**
* 把新加的节点放在链表的最后面
*/
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
/**
* 继承于HashMap的Entry,添加了上一个和下一个节点由单向链表变成了双向链表
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
/**
* 这个方法是在调用put的时候,hashMap中的putVal()方法中的afterNodeAccess重写的
* 在put的时候已经有值了会更新值然后调用此方法
* 把当前节点移动到双向链表的最末端
*/
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
//accessOrder默认是false的
//e不在最末尾就修改链表,将e移到链表最末尾的操作
if (accessOrder && (last = tail) != e) {
//b是e的前一个节点,a是e的后一个节点
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
//修改计数器自加1
++modCount;
}
}
/**
* 插入完成会掉用此方法,传过来的是false
*/
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
/**
* 这个方法一直返回的是false,那就是不需要删除末尾节点,
* 自己写的时候可以重写给定一个条件控制LinkedHashMap中最老数据在何时删除
*/
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
/**
* 虽然没有重写put方法,但是重写了get方法
*/
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
//如果启动了访问模式就调用
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
这个有啥用啊?写了那么多。。作为小白我以前自己开发的时候从来没想过那么多,看到大家都在讨论图片缓存,所以看了一下网上的代码。做出了一些思考,我们显示图片是可以先从内存中获取,然后内存中没有就去本地缓存文件找,本地也没有就去网络获取。这样可以减少服务器的压力,不要每次都访问服务器。
使用方法
private LruCache<String, Bitmap> mMemoryCache;
public ImageDownLoader(Context context){
//获取系统分配给每个应用程序的最大内存,不同的厂商手机给的最大值不一样
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int mCacheSize = maxMemory / 8;
//给LruCache分配1/8
mMemoryCache = new LruCache<String, Bitmap>(mCacheSize){
/**
* 必须重写此方法,来测量Bitmap的大小
*/
@Override
protected int sizeOf(String key, Bitmap value) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return value.getAllocationByteCount();
}
return value.getHeight() * value.getRowBytes();
}
/**
* 当缓存被移除时调用,可不重写
* @param evicted 缓存移除的原因,true表示内存不够移除了,false表示put移除或者remove移除了
* @param key key
* @param oldValue
* @param newValue
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
}
/**
* 获取不到的时候调用,可不重写
* @param key
* @return
*/
@Override
protected Bitmap create(String key) {
return super.create(key);
}
};
}
//添加到缓存
mMemoryCache.put(key, bitmap);
//获取缓存
mMemoryCache.get(key);
看一下LruCache
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
//直接new了一个LinkedHashMap,把accessOrder设为true,也就是按访问顺序排序
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
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);
}
}
//有旧值就执行entryRemoved方法,这是一个空实现,可以自己重写
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
/**
* 看到这我们就知道为什么需要重写了
*/
protected int sizeOf(K key, V value) {
return 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!");
}
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就是说被迫移除
entryRemoved(true, key, value, null);
}
}
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//从LinkedHashMap中获取,获取方式还是hashMap的查找方式,只是内部维护一个链表做一些删除元素的操作。
mapValue = map.get(key);
if (mapValue != null) {
//命中数自加
hitCount++;
return mapValue;
}
//miss数自加
missCount++;
}
//这个create方法可重写,也就是LinkedHashMap中没有的时候会调用到这里
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
//发生冲突,取消最后一次添加
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
//调用entryRemoved,这里false表示内存
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
最后自己画一个图总结了一下,有错误请指正。共同进步学习。
原理图
网友评论