首页 > 代码库 > Java技术之LinkedHashMap
Java技术之LinkedHashMap
在Android开发过程中,遇到需要处理大量图片显示问题时,需要运用缓存机制。而Java中已经为我们提供了很好的工具LinkedHashMap,为我们实现LRU算法提供了很大的便利。下面结合LinkedHashMap源码来了解其原理。
1.LinkedHashMap结构
LinkedHashMap继承了HashMap底层是通过Hash表+单向链表实现Hash算法,内部自己维护了一套元素访问顺序的列表。
LinkedHashMap与HashMap的区别是在于LinkeedHashMap每个元素插入表中时,就会并入到双向链表中。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。当访问顺序时,每次调用get或者put,受到影响的条目将从当前的位置删除,并放到链表条目的尾部,在这里只是链表中位置变化受到影响,而散列表中的桶不会受影响。
2.LinkedHashMap实现
2.1记录顺序
Linked内部含有一个private transient Entry header;来记录元素插入的顺序或者是元素被访问的顺序。
决定记录顺序的是由初始化时private final boolean accessOrder;
AccessOrder。默认为false,即插入顺序,true为访问顺序。
2.2构造函数
1 public LinkedHashMap(int initialCapacity,2 float loadFactor,3 boolean accessOrder) {4 super(initialCapacity, loadFactor);5 this.accessOrder = accessOrder;6 }
initialCapacity是初始化表的大小,loadFactor是装载因子,默认0.75,accessOrder是指定记录的顺序,访问顺序还是插入顺序。后面的true表明LinkedHashMap按照访问的次序来排序。按照访问的次序来排序的含义:当调用LinkedHashMap的get(key)或者put(key, value)时,碰巧key在map中被包含,那么LinkedHashMap会将key对象的entry放在线性结构的最后。按照插入顺序来排序的含义:调用get(key), 或者put(key, value)并不会对线性结构产生任何的影响。
2.3获取记录
1 public V get(Object key) {2 Entry<K,V> e = (Entry<K,V>)getEntry(key);3 if (e == null)4 return null;5 e.recordAccess(this);6 return e.value;7 }
如果key存在,执行recordAccess(this);
1 void recordAccess(HashMap<K,V> m) {2 LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;3 if (lm.accessOrder) {4 lm.modCount++;5 remove();6 addBefore(lm.header);7 }8 }
recordAccess方法会accessOrder为true会先调用remove清除当前首尾元素的指向关系,之后调用addBefore方法,将当前元素加入header之前。
1 /** 2 * Removes this entry from the linked list. 3 */ 4 private void remove() { 5 before.after = after; 6 after.before = before; 7 } 8 9 /**10 * Inserts this entry before the specified existing entry in the list.11 */12 private void addBefore(Entry<K,V> existingEntry) {13 after = existingEntry;14 before = existingEntry.before;15 before.after = this;16 after.before = this;17 }
2.4获取数据
LinkedHashMap put方法时继承HashMap的put方法,但是需要重写其中的方法
1 public V put(K key, V value) { 2 if (key == null) 3 return putForNullKey(value); 4 int hash = hash(key.hashCode()); 5 int i = indexFor(hash, table.length); 6 for (Entry<K,V> e = table[i]; e != null; e = e.next) { 7 Object k; 8 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 9 V oldValue =http://www.mamicode.com/ e.value;10 e.value =http://www.mamicode.com/ value;11 e.recordAccess(this);12 return oldValue;13 }14 }15 16 modCount++;17 addEntry(hash, key, value, i);18 return null;19 }
2.5删除最久未被访问数据
1 protected boolean removeEldestEntry(Map.Entry eldest) {2 return false;3 }
LinkedHashMap中一个方法,当调用put(key, value)的时候,HashMap判断是否要自动增加map的size的作法是判断是否超过threshold, LinkedHashMap则进行了扩展,如果removeEldestEntry方法return false;(默认的实现),那么LinkedHashMap跟HashMap处理扩容的方式一致;如果removeEldestEntry返回 true,那么LinkedHashMap会自动删掉最不常用的那个entry(也就是header线性表最前面的那个)。这一特点可以很好实现缓存机制。
3.缓存机制简单实现
1 import java.util.Iterator; 2 import java.util.LinkedHashMap; 3 4 public class LRULinkedHashMap<K,V> extends LinkedHashMap<K, V>{ 5 6 private static final int INIT_CAPACITY=10; 7 private static final int MAX_SIZE=6; 8 private static final float LOAD_FACTOR=0.75f; 9 10 public LRULinkedHashMap() {11 super(INIT_CAPACITY, LOAD_FACTOR, true);12 }13 14 @Override15 protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {16 return size()>MAX_SIZE;17 }18 19 public static void main(String[] args) {20 21 LRULinkedHashMap<String, String> lruLinkedHashMap=new LRULinkedHashMap<String,String>();22 lruLinkedHashMap.put("1", "A");23 lruLinkedHashMap.put("2", "B");24 lruLinkedHashMap.put("3", "C");25 lruLinkedHashMap.put("4", "D");26 lruLinkedHashMap.put("5", "E");27 lruLinkedHashMap.put("6", "F");28 lruLinkedHashMap.put("7", "G");29 lruLinkedHashMap.put("8", "H");30 lruLinkedHashMap.put("9", "I");31 lruLinkedHashMap.put("10", "J");32 lruLinkedHashMap.put("11", "K");33 Iterator<String> iterator=lruLinkedHashMap.keySet().iterator();34 while(iterator.hasNext()){35 System.out.print(iterator.next()+" ");36 }37 }38 39 }
结果显示:
6 7 8 9 10 11