(1).LRU算法
百度百科中对LRU算法的解释如下:
LRU是(Least Recently Used)近期最少使用算法。
其实就是把近期最少使用的内容淘汰出局。LRU是一种缓存算法,在空间有限的情况下,把使用频率最低的内容淘汰出缓存,让使用频率高的内容留在缓存里。
(2)在看项目框架的代码时,看到了LRUMap这个类,是apache-commons下的类,由于是第一次听说LRU算法,所以就决定研究一下它的源码。
(3)先来看一下例子:
1 public class TestLRUMap { 2 public static void main(String[] args) { 3 LRUMap<Integer, Integer> map = new LRUMap<Integer, Integer>(3); 4 map.put(1, 1); 5 map.put(2, 2); 6 map.put(3, 3); 7 map.put(4, 4); 8 System.out.println(map); 9 } 10 }
输出:{2=2, 3=3, 4=4}
增加一行代码,再看结果:
1 public class TestLRUMap { 2 public static void main(String[] args) { 3 LRUMap<Integer, Integer> map = new LRUMap<Integer, Integer>(3); 4 map.put(1, 1); 5 map.put(2, 2); 6 map.put(3, 3); 7 map.get(1); 8 map.put(4, 4); 9 System.out.println(map); 10 } 11 }
输出:{3=3, 1=1, 4=4}
两个结果为什么不一样呢?因为第二个例子,我们添加了一行代码:map.get(1);代表1元素是最新使用的,而相应的2就是最长时间没有使用的元素,而map的长度又限制为3,所以2就被删掉了。
(4)下面分析源码:
首先看get方法:
1 public V get(final Object key) { 2 final LinkEntry<K, V> entry = getEntry(key); 3 if (entry == null) { 4 return null; 5 } 6 //主要看这个方法 7 moveToMRU(entry); 8 return entry.getValue(); 9 }
moveToMRUst方法:
1 protected void moveToMRU(final LinkEntry<K, V> entry) { 2 if (entry.after != header) { 3 modCount++; 4 // remove 5 if(entry.before == null) { 6 throw new IllegalStateException("Entry.before is null." + 7 " Please check that your keys are immutable, and that you have used synchronization properly." + 8 " If so, then please report this to dev@commons.apache.org as a bug."); 9 } 10 //其实就是链表指正的改变,使得被get的元素排到链表中header前面 11 entry.before.after = entry.after; 12 entry.after.before = entry.before; 13 // add first 14 entry.after = header; 15 entry.before = header.before; 16 header.before.after = entry; 17 header.before = entry; 18 } else if (entry == header) { 19 throw new IllegalStateException("Can't move header to MRU" + 20 " (please report this to dev@commons.apache.org)"); 21 } 22 }
接下来看put方法:
1 public V put(final K key, final V value) { 2 //如果key为null,则将其转换为new Object() 3 final Object convertedKey = convertKey(key); 4 //获得key的hashCode 5 final int hashCode = hash(convertedKey); 6 //根据hashCode得到其所在数组中的下标 7 final int index = hashIndex(hashCode, data.length); 8 //获得数组对应下标的HashEntry对象 9 HashEntry<K, V> entry = data[index]; 10 while (entry != null) { 11 //如果entry不为空,则判断是否已经有相同key的元素存在 12 if (entry.hashCode == hashCode && isEqualKey(convertedKey, entry.key)) { 13 //判断是否有相同的key存在,如果有相同的key存在,则替换其值,但不会改变其在链表中的位置 14 final V oldValue = entry.getValue(); 15 updateEntry(entry, value); 16 return oldValue; 17 } 18 entry = entry.next; 19 } 20 //如果entry为空,说明这个下标上还没有HashEntry对象 21 addMapping(index, hashCode, key, value); 22 return null; 23 } 24 25 26 protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) { 27 //如果map已满,则执行删除到目前为止最长时间没有使用的元素 28 if (isFull()) { 29 //由于LRUMap每次添加元素的时候都放在链表中header的前面,所以,header后面的元素是删除的对象 30 LinkEntry<K, V> reuse = header.after; 31 boolean removeLRUEntry = false; 32 if (scanUntilRemovable) { 33 while (reuse != header && reuse != null) { 34 if (removeLRU(reuse)) { 35 removeLRUEntry = true; 36 break; 37 } 38 reuse = reuse.after; 39 } 40 if (reuse == null) { 41 throw new IllegalStateException( 42 "Entry.after=null, header.after" + header.after + " header.before" + header.before + 43 " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + 44 " Please check that your keys are immutable, and that you have used synchronization properly." + 45 " If so, then please report this to dev@commons.apache.org as a bug."); 46 } 47 } else { 48 removeLRUEntry = removeLRU(reuse); 49 } 50 51 if (removeLRUEntry) { 52 if (reuse == null) { 53 throw new IllegalStateException( 54 "reuse=null, header.after=" + header.after + " header.before" + header.before + 55 " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + 56 " Please check that your keys are immutable, and that you have used synchronization properly." + 57 " If so, then please report this to dev@commons.apache.org as a bug."); 58 } 59 //执行LRUMap的reuseMapping方法 60 reuseMapping(reuse, hashIndex, hashCode, key, value); 61 } else { 62 super.addMapping(hashIndex, hashCode, key, value); 63 } 64 } else { 65 super.addMapping(hashIndex, hashCode, key, value); 66 } 67 } 68 69 70 protected void reuseMapping(final LinkEntry<K, V> entry, final int hashIndex, final int hashCode, 71 final K key, final V value) { 72 // find the entry before the entry specified in the hash table 73 // remember that the parameters (except the first) refer to the new entry, 74 // not the old one 75 try { 76 //获得删除元素在数组中的下标 77 final int removeIndex = hashIndex(entry.hashCode, data.length); 78 final HashEntry<K, V>[] tmp = data; // may protect against some sync issues 79 HashEntry<K, V> loop = tmp[removeIndex]; 80 HashEntry<K, V> previous = null; 81 //loop的作用是防止map被其他线程修改了导致要删除的元素已经被其他线程给删了 82 while (loop != entry && loop != null) { 83 previous = loop; 84 loop = loop.next; 85 } 86 if (loop == null) { 87 throw new IllegalStateException( 88 "Entry.next=null, data[removeIndex]=" + data[removeIndex] + " previous=" + previous + 89 " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + 90 " Please check that your keys are immutable, and that you have used synchronization properly." + 91 " If so, then please report this to dev@commons.apache.org as a bug."); 92 } 93 94 // reuse the entry 95 modCount++; 96 removeEntry(entry, removeIndex, previous); 97 reuseEntry(entry, hashIndex, hashCode, key, value); 98 addEntry(entry, hashIndex); 99 } catch (final NullPointerException ex) { 100 throw new IllegalStateException( 101 "NPE, entry=" + entry + " entryIsHeader=" + (entry==header) + 102 " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + 103 " Please check that your keys are immutable, and that you have used synchronization properly." + 104 " If so, then please report this to dev@commons.apache.org as a bug."); 105 } 106 }
好了,通过源码分析,终于理解了LRUMap是怎么实现LRU算法的。