• LRUMap源码分析


    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算法的。

     

  • 相关阅读:
    A good habit is half done
    mysql 练习题
    管理的实践
    mysql 表关联时执行顺序
    python 实现短信轰炸
    python django码云第三方登录
    mysql事务的隔离级别
    微服务
    什么是git
    如何使用Hexo创建博客
  • 原文地址:https://www.cnblogs.com/huashui/p/3353557.html
Copyright © 2020-2023  润新知