zoukankan      html  css  js  c++  java
  • 简单LRU算法实现缓存

       最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示:

    java 代码
     
    1. import java.util.ArrayList;  
    2. import java.util.Collection;  
    3. import java.util.LinkedHashMap;  
    4. import java.util.concurrent.locks.Lock;  
    5. import java.util.concurrent.locks.ReentrantLock;  
    6. import java.util.Map;  
    7.   
    8.   
    9. /** 
    10.  * 类说明:利用LinkedHashMap实现简单的缓存, 必须实现removeEldestEntry方法,具体参见JDK文档 
    11.  *  
    12.  * @author dennis 
    13.  *  
    14.  * @param <K> 
    15.  * @param <V> 
    16.  */  
    17. public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {  
    18.     private final int maxCapacity;  
    19.   
    20.     private static final float DEFAULT_LOAD_FACTOR = 0.75f;  
    21.   
    22.     private final Lock lock = new ReentrantLock();  
    23.   
    24.     public LRULinkedHashMap(int maxCapacity) {  
    25.         super(maxCapacity, DEFAULT_LOAD_FACTOR, true);  
    26.         this.maxCapacity = maxCapacity;  
    27.     }  
    28.   
    29.     @Override  
    30.     protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {  
    31.         return size() > maxCapacity;  
    32.     }  
    33.     @Override  
    34.     public boolean containsKey(Object key) {  
    35.         try {  
    36.             lock.lock();  
    37.             return super.containsKey(key);  
    38.         } finally {  
    39.             lock.unlock();  
    40.         }  
    41.     }  
    42.   
    43.       
    44.     @Override  
    45.     public V get(Object key) {  
    46.         try {  
    47.             lock.lock();  
    48.             return super.get(key);  
    49.         } finally {  
    50.             lock.unlock();  
    51.         }  
    52.     }  
    53.   
    54.     @Override  
    55.     public V put(K key, V value) {  
    56.         try {  
    57.             lock.lock();  
    58.             return super.put(key, value);  
    59.         } finally {  
    60.             lock.unlock();  
    61.         }  
    62.     }  
    63.   
    64.     public int size() {  
    65.         try {  
    66.             lock.lock();  
    67.             return super.size();  
    68.         } finally {  
    69.             lock.unlock();  
    70.         }  
    71.     }  
    72.   
    73.     public void clear() {  
    74.         try {  
    75.             lock.lock();  
    76.             super.clear();  
    77.         } finally {  
    78.             lock.unlock();  
    79.         }  
    80.     }  
    81.   
    82.     public Collection<Map.Entry<K, V>> getAll() {  
    83.         try {  
    84.             lock.lock();  
    85.             return new ArrayList<Map.Entry<K, V>>(super.entrySet());  
    86.         } finally {  
    87.             lock.unlock();  
    88.         }  
    89.     }  
    90. }  
    91.     

      如果你去看LinkedHashMap的源码可知,LRU算法是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入 的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置。
        LRU算法还可以通过计数来实现,缓存存储的位置附带一个计数器,当命中时将计数器加1,替换时就查找计数最小的位置并替换,结合访问时间戳来实现。这种 算法比较适合缓存数据量较小的场景,显然,遍历查找计数最小位置的时间复杂度为O(n)。我实现了一个,结合了访问时间戳,当最小计数大于 MINI_ACESS时,就移除最久没有被访问的项:
    java 代码
     
    1. import java.io.Serializable;  
    2. import java.util.ArrayList;  
    3. import java.util.Collection;  
    4. import java.util.HashMap;  
    5. import java.util.Iterator;  
    6. import java.util.Map;  
    7. import java.util.Set;  
    8. import java.util.concurrent.atomic.AtomicInteger;  
    9. import java.util.concurrent.atomic.AtomicLong;  
    10. import java.util.concurrent.locks.Lock;  
    11. import java.util.concurrent.locks.ReentrantLock;  
    12.   
    13. /** 
    14.  *  
    15.  * @author dennis  
    16.  * 类说明:当缓存数目不多时,才用缓存计数的传统LRU算法 
    17.  * @param <K> 
    18.  * @param <V> 
    19.  */  
    20. public class LRUCache<K, V> implements Serializable {  
    21.   
    22.     private static final int DEFAULT_CAPACITY = 100;  
    23.   
    24.     protected Map<K, ValueEntry> map;  
    25.   
    26.     private final Lock lock = new ReentrantLock();  
    27.   
    28.     private final transient int maxCapacity;  
    29.   
    30.     private static int MINI_ACCESS = 10;  
    31.   
    32.     public LRUCache() {  
    33.         this(DEFAULT_CAPACITY);  
    34.     }  
    35.   
    36.     public LRUCache(int capacity) {  
    37.         if (capacity <= 0)  
    38.             throw new RuntimeException("缓存容量不得小于0");  
    39.         this.maxCapacity = capacity;  
    40.         this.map = new HashMap<K, ValueEntry>(maxCapacity);  
    41.     }  
    42.   
    43.     public boolean ContainsKey(K key) {  
    44.         try {  
    45.             lock.lock();  
    46.             return this.map.containsKey(key);  
    47.         } finally {  
    48.             lock.unlock();  
    49.         }  
    50.     }  
    51.   
    52.     public V put(K key, V value) {  
    53.         try {  
    54.             lock.lock();  
    55.             if ((map.size() > maxCapacity - 1) && !map.containsKey(key)) {  
    56.                 // System.out.println("开始");  
    57.                 Set<Map.Entry<K, ValueEntry>> entries = this.map.entrySet();  
    58.                 removeRencentlyLeastAccess(entries);  
    59.             }  
    60.             ValueEntry valueEntry = map.put(key, new ValueEntry(value));  
    61.             if (valueEntry != null)  
    62.                 return valueEntry.value;  
    63.             else  
    64.                 return null;  
    65.         } finally {  
    66.             lock.unlock();  
    67.         }  
    68.     }  
    69.   
    70.     /** 
    71.      * 移除最近最少访问 
    72.      */  
    73.     protected void removeRencentlyLeastAccess(  
    74.             Set<Map.Entry<K, ValueEntry>> entries) {  
    75.         // 最小使用次数  
    76.         int least = 0;  
    77.         // 最久没有被访问  
    78.         long earliest = 0;  
    79.         K toBeRemovedByCount = null;  
    80.         K toBeRemovedByTime = null;  
    81.         Iterator<Map.Entry<K, ValueEntry>> it = entries.iterator();  
    82.         if (it.hasNext()) {  
    83.             Map.Entry<K, ValueEntry> valueEntry = it.next();  
    84.             least = valueEntry.getValue().count.get();  
    85.             toBeRemovedByCount = valueEntry.getKey();  
    86.             earliest = valueEntry.getValue().lastAccess.get();  
    87.             toBeRemovedByTime = valueEntry.getKey();  
    88.         }  
    89.         while (it.hasNext()) {  
    90.             Map.Entry<K, ValueEntry> valueEntry = it.next();  
    91.             if (valueEntry.getValue().count.get() < least) {  
    92.                 least = valueEntry.getValue().count.get();  
    93.                 toBeRemovedByCount = valueEntry.getKey();  
    94.             }  
    95.             if (valueEntry.getValue().lastAccess.get() < earliest) {  
    96.                 earliest = valueEntry.getValue().count.get();  
    97.                 toBeRemovedByTime = valueEntry.getKey();  
    98.             }  
    99.         }  
    100.         // System.out.println("remove:" + toBeRemoved);  
    101.         // 如果最少使用次数大于MINI_ACCESS,那么移除访问时间最早的项(也就是最久没有被访问的项)  
    102.         if (least > MINI_ACCESS) {  
    103.             map.remove(toBeRemovedByTime);  
    104.         } else {  
    105.             map.remove(toBeRemovedByCount);  
    106.         }  
    107.     }  
    108.   
    109.     public V get(K key) {  
    110.         try {  
    111.             lock.lock();  
    112.             V value = null;  
    113.             ValueEntry valueEntry = map.get(key);  
    114.             if (valueEntry != null) {  
    115.                 // 更新访问时间戳  
    116.                 valueEntry.updateLastAccess();  
    117.                 // 更新访问次数  
    118.                 valueEntry.count.incrementAndGet();  
    119.                 value = valueEntry.value;  
    120.             }  
    121.             return value;  
    122.         } finally {  
    123.             lock.unlock();  
    124.         }  
    125.     }  
    126.   
    127.     public void clear() {  
    128.         try {  
    129.             lock.lock();  
    130.             map.clear();  
    131.         } finally {  
    132.             lock.unlock();  
    133.         }  
    134.     }  
    135.   
    136.     public int size() {  
    137.         try {  
    138.             lock.lock();  
    139.             return map.size();  
    140.         } finally {  
    141.             lock.unlock();  
    142.         }  
    143.     }  
    144.   
    145.     public Collection<Map.Entry<K, V>> getAll() {  
    146.         try {  
    147.             lock.lock();  
    148.             Set<K> keys = map.keySet();  
    149.             Map<K, V> tmp = new HashMap<K, V>();  
    150.             for (K key : keys) {  
    151.                 tmp.put(key, map.get(key).value);  
    152.             }  
    153.             return new ArrayList<Map.Entry<K, V>>(tmp.entrySet());  
    154.         } finally {  
    155.             lock.unlock();  
    156.         }  
    157.     }  
    158.   
    159.     class ValueEntry implements Serializable {  
    160.         private V value;  
    161.   
    162.         private AtomicInteger count;  
    163.   
    164.         private AtomicLong lastAccess;  
    165.   
    166.         public ValueEntry(V value) {  
    167.             this.value = value;  
    168.             this.count = new AtomicInteger(0);  
    169.             lastAccess = new AtomicLong(System.nanoTime());  
    170.         }  
    171.           
    172.         public void updateLastAccess() {  
    173.             this.lastAccess.set(System.nanoTime());  
    174.         }  
    175.   
    176.     }  
    177. }  
  • 相关阅读:
    安装lamp服务器
    Linux ssh登录命令
    一些替代Xshell的软件推荐
    字符串输入的几种方式
    Java数据库操作的一些注意
    模拟堆
    web安全基础第一天
    情报搜集
    kali中的postgres怎么连接
    LeetCode:Validate Binary Search Tree
  • 原文地址:https://www.cnblogs.com/wangzehuaw/p/4318038.html
Copyright © 2011-2022 走看看