缓存 LRU- java的版本

来源:互联网 发布:算法研发工程师 编辑:程序博客网 时间:2024/04/29 14:50
一、使用LinkedHashMap实现

//Copyright 2007 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
//www.source-code.biz, www.inventec.ch/chdh
//
//This module is multi-licensed and may be used under the terms
//of any of the following licenses:
//
//EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
//LGPL, GNU Lesser General Public License, V2 or later, http://www.gnu.org/licenses/lgpl.html
//GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html
//AL, Apache License, V2.0 or later, http://www.apache.org/licenses
//BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
//
//Please contact the author if you need another license.
//This module is provided "as is", without warranties of any kind.
/**
 *  LinkedHashMap。 用这个类有两大好处:
 *  一是它本身已经实现了按照访问顺序的存储,也就是说,最近读取的会放在最前面,最最不常读取的会放在最后
 *  (当然,它也可以实现按照插入顺序存储)。
 *  第二,LinkedHashMap本身有一个方法用于判断是否需要移除最不常读取的数,但是,原始方法默认不需要移除
 *  (这是,LinkedHashMap相当于一个linkedlist),
 *  所以,我们需要override这样一个方法,使得当缓存里存放的数据个数超过规定个数后,就把最不常用的移除掉。
 */

import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.Map;
import java.util.ArrayList;

/**
* An LRU cache, based on <code>LinkedHashMap</code>.
*
* <p>
* This cache has a fixed maximum number of elements (<code>cacheSize</code>).
* If the cache is full and another entry is added, the LRU (least recently used) entry is dropped.
*
* <p>
* This class is thread-safe. All methods of this class are synchronized.
*
* <p>
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
* Multi-licensed: EPL / LGPL / GPL / AL / BSD.
*/
public class LRUCache<K,V> {

private static final float   hashTableLoadFactor = 0.75f;

private LinkedHashMap<K,V>   map;
private int                  cacheSize;

/**
* Creates a new LRU cache.
* @param cacheSize the maximum number of entries that will be kept in this cache.
*/
public LRUCache (int cacheSize) {
this.cacheSize = cacheSize;
int hashTableCapacity = (int)Math.ceil(cacheSize / hashTableLoadFactor) + 1;
map = new LinkedHashMap<K,V>(hashTableCapacity, hashTableLoadFactor, true) {
// (an anonymous inner class)
private static final long serialVersionUID = 1;
    @Override protected boolean removeEldestEntry (Map.Entry<K,V> eldest) {
    return size() > LRUCache.this.cacheSize; 
    }
   }; 
      }

/**
* Retrieves an entry from the cache.<br>
* The retrieved entry becomes the MRU (most recently used) entry.
* @param key the key whose associated value is to be returned.
* @return    the value associated to this key, or null if no value with this key exists in the cache.
*/
public synchronized V get (K key) {
return map.get(key); 
}

/**
* Adds an entry to this cache.
* The new entry becomes the MRU (most recently used) entry.
* If an entry with the specified key already exists in the cache, it is replaced by the new entry.
* If the cache is full, the LRU (least recently used) entry is removed from the cache.
* @param key    the key with which the specified value is to be associated.
* @param value  a value to be associated with the specified key.
*/
public synchronized void put (K key, V value) {
map.put (key, value); 
}

/**
* Clears the cache.
*/
public synchronized void clear() {
map.clear(); 
}

/**
* Returns the number of used entries in the cache.
* @return the number of entries currently in the cache.
*/
public synchronized int usedEntries() {
return map.size(); 
}

/**
* Returns a <code>Collection</code> that contains a copy of all cache entries.
* @return a <code>Collection</code> with a copy of the cache content.
*/
public synchronized Collection<Map.Entry<K,V>> getAll() {
return new ArrayList<Map.Entry<K,V>>(map.entrySet()); 
}

} // end class LRUCache

或直接使用(推荐上面的方式,上面的方法更优雅一些)
final int cacheSize = 100;
Map<String, String> map = new LinkedHashMap<String, String>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
    @Override
    protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
    return size() > cacheSize;
    }
};
java源码分析之LinkedHashMap参考 : http://blog.csdn.net/jzhf2012/article/details/8540688

二、基于双链表的LRU算法的实现(tomcat6之前版本用到的org.apache.tomcat.util.collections.LRUCache
  1. public class LRUCache {  
  2.     /** 
  3.      * 链表节点 
  4.      * @author Administrator 
  5.      * 
  6.      */  
  7.     class CacheNode {  
  8.         CacheNode prev;//前一节点  
  9.         CacheNode next;//后一节点  
  10.         Object value;//值  
  11.         Object key;//键  
  12.         CacheNode() {  
  13.         }  
  14.     }  
  15.   
  16.     public LRUCache(int i) {  
  17.         currentSize = 0;  
  18.         cacheSize = i;  
  19.         nodes = new Hashtable(i);//缓存容器  
  20.     }  
  21.       
  22.     /** 
  23.      * 获取缓存中对象 
  24.      * @param key 
  25.      * @return 
  26.      */  
  27.     public Object get(Object key) {  
  28.         CacheNode node = (CacheNode) nodes.get(key);  
  29.         if (node != null) {  
  30.             moveToHead(node);  
  31.             return node.value;  
  32.         } else {  
  33.             return null;  
  34.         }  
  35.     }  
  36.       
  37.     /** 
  38.      * 添加缓存 
  39.      * @param key 
  40.      * @param value 
  41.      */  
  42.     public void put(Object key, Object value) {  
  43.         CacheNode node = (CacheNode) nodes.get(key);  
  44.           
  45.         if (node == null) {  
  46.             //缓存容器是否已经超过大小.  
  47.             if (currentSize >= cacheSize) {  
  48.                 if (last != null)//将最少使用的删除  
  49.                     nodes.remove(last.key);  
  50.                 removeLast();  
  51.             } else {  
  52.                 currentSize++;  
  53.             }  
  54.               
  55.             node = new CacheNode();  
  56.         }  
  57.         node.value = value;  
  58.         node.key = key;  
  59.         //将最新使用的节点放到链表头,表示最新使用的.  
  60.         moveToHead(node);  
  61.         nodes.put(key, node);  
  62.     }  
  63.   
  64.     /** 
  65.      * 将缓存删除 
  66.      * @param key 
  67.      * @return 
  68.      */  
  69.     public Object remove(Object key) {  
  70.         CacheNode node = (CacheNode) nodes.get(key);  
  71.         if (node != null) {  
  72.             if (node.prev != null) {  
  73.                 node.prev.next = node.next;  
  74.             }  
  75.             if (node.next != null) {  
  76.                 node.next.prev = node.prev;  
  77.             }  
  78.             if (last == node)  
  79.                 last = node.prev;  
  80.             if (first == node)  
  81.                 first = node.next;  
  82.         }  
  83.         return node;  
  84.     }  
  85.   
  86.     public void clear() {  
  87.         first = null;  
  88.         last = null;  
  89.     }  
  90.   
  91.     /** 
  92.      * 删除链表尾部节点 
  93.      *  表示 删除最少使用的缓存对象 
  94.      */  
  95.     private void removeLast() {  
  96.         //链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)  
  97.         if (last != null) {  
  98.             if (last.prev != null)  
  99.                 last.prev.next = null;  
  100.             else  
  101.                 first = null;  
  102.             last = last.prev;  
  103.         }  
  104.     }  
  105.       
  106.     /** 
  107.      * 移动到链表头,表示这个节点是最新使用过的 
  108.      * @param node 
  109.      */  
  110.     private void moveToHead(CacheNode node) {  
  111.         if (node == first)  
  112.             return;  
  113.         if (node.prev != null)  
  114.             node.prev.next = node.next;  
  115.         if (node.next != null)  
  116.             node.next.prev = node.prev;  
  117.         if (last == node)  
  118.             last = node.prev;  
  119.         if (first != null) {  
  120.             node.next = first;  
  121.             first.prev = node;  
  122.         }  
  123.         first = node;  
  124.         node.prev = null;  
  125.         if (last == null)  
  126.             last = first;  
  127.     }  
  128.     private int cacheSize;  
  129.     private Hashtable nodes;//缓存容器  
  130.     private int currentSize;  
  131.     private CacheNode first;//链表头  
  132.     private CacheNode last;//链表尾  
  133. }  
加锁的 一种写法:

  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.Map.Entry;  
  8. import java.util.Set;  
  9. import java.util.concurrent.atomic.AtomicLong;  
  10. import java.util.concurrent.locks.Lock;  
  11. import java.util.concurrent.locks.ReadWriteLock;  
  12. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  13.   
  14. /** 
  15.  *  
  16.  * 类说明:当缓存数目不多时,才用缓存计数的传统LRU算法 
  17.  *  
  18.  * @param 
  19.  * @param 
  20.  */  
  21. public class LRUCache<K, V> implements Serializable {  
  22.   
  23.     /** 
  24.      *  
  25.      */  
  26.     private static final long serialVersionUID = 2727577376847051556L;  
  27.   
  28.     private static final int DEFAULT_CAPACITY = 100;  
  29.   
  30.     protected Map<K, ValueEntry> map;  
  31.   
  32.     private final ReadWriteLock lock = new ReentrantReadWriteLock();  
  33.   
  34.     private final Lock readLock = lock.readLock();  
  35.   
  36.     private final Lock writeLock = lock.writeLock();  
  37.   
  38.     private volatile int maxCapacity; // 保持可见性  
  39.   
  40.     public static int MINI_ACCESS = 5;  
  41.   
  42.     public LRUCache() {  
  43.         this(DEFAULT_CAPACITY);  
  44.     }  
  45.   
  46.     public LRUCache(int capacity) {  
  47.         if (capacity <= 0)  
  48.             throw new RuntimeException("缓存容量不得小于0");  
  49.         this.maxCapacity = capacity;  
  50.         this.map = new HashMap<K, ValueEntry>(maxCapacity);  
  51.     }  
  52.   
  53.     public boolean ContainsKey(K key) {  
  54.         try {  
  55.             readLock.lock();  
  56.             return this.map.containsKey(key);  
  57.         } finally {  
  58.             readLock.unlock();  
  59.         }  
  60.     }  
  61.   
  62.     public V put(K key, V value) {  
  63.         try {  
  64.             writeLock.lock();  
  65.             if ((map.size() > maxCapacity - 1) && !map.containsKey(key)) {  
  66.                 // System.out.println("开始");  
  67.                 Set<Entry<K, ValueEntry>> entries = this.map.entrySet();  
  68.                 removeRencentlyLeastAccess(entries);  
  69.             }  
  70.             ValueEntry new_value = new ValueEntry(value);  
  71.             ValueEntry old_value = map.put(key, new_value);  
  72.             if (old_value != null) {  
  73.                 new_value.count = old_value.count;  
  74.                 return old_value.value;  
  75.             } else  
  76.                 return null;  
  77.         } finally {  
  78.             writeLock.unlock();  
  79.         }  
  80.     }  
  81.   
  82.     /** 
  83.      * 移除最近最少访问 
  84.      */  
  85.     protected V removeRencentlyLeastAccess(Set<Map.Entry<K, ValueEntry>> entries) {  
  86.         // 最小使用次数  
  87.         long least = 0;  
  88.         // 访问时间最早  
  89.         long earliest = 0;  
  90.         K toBeRemovedByCount = null;  
  91.         K toBeRemovedByTime = null;  
  92.         Iterator<Map.Entry<K, ValueEntry>> it = entries.iterator();  
  93.         if (it.hasNext()) {  
  94.             Map.Entry<K, ValueEntry> valueEntry = it.next();  
  95.             least = valueEntry.getValue().count.get();  
  96.             toBeRemovedByCount = valueEntry.getKey();  
  97.             earliest = valueEntry.getValue().lastAccess.get();  
  98.             toBeRemovedByTime = valueEntry.getKey();  
  99.         }  
  100.         while (it.hasNext()) {  
  101.             Map.Entry<K, ValueEntry> valueEntry = it.next();  
  102.             if (valueEntry.getValue().count.get() < least) {  
  103.                 least = valueEntry.getValue().count.get();  
  104.                 toBeRemovedByCount = valueEntry.getKey();  
  105.             }  
  106.             if (valueEntry.getValue().lastAccess.get() < earliest) {  
  107.                 earliest = valueEntry.getValue().count.get();  
  108.                 toBeRemovedByTime = valueEntry.getKey();  
  109.             }  
  110.         }  
  111.         // System.out.println("remove:" + toBeRemoved);  
  112.         // 如果最少使用次数大于MINI_ACCESS,那么移除访问时间最早的项(也就是最久没有被访问的项)  
  113.         if (least > MINI_ACCESS) {  
  114.             return map.remove(toBeRemovedByTime).value;  
  115.         } else {  
  116.             return map.remove(toBeRemovedByCount).value;  
  117.         }  
  118.     }  
  119.   
  120.     public V get(K key) {  
  121.         try {  
  122.             readLock.lock();  
  123.             V value = null;  
  124.             ValueEntry valueEntry = map.get(key);  
  125.             if (valueEntry != null) {  
  126.                 // 更新访问时间戳  
  127.                 valueEntry.updateLastAccess();  
  128.                 // 更新访问次数  
  129.                 valueEntry.count.incrementAndGet();  
  130.                 value = valueEntry.value;  
  131.             }  
  132.             return value;  
  133.         } finally {  
  134.             readLock.unlock();  
  135.         }  
  136.     }  
  137.   
  138.     public void clear() {  
  139.         try {  
  140.             writeLock.lock();  
  141.             map.clear();  
  142.         } finally {  
  143.             writeLock.unlock();  
  144.         }  
  145.     }  
  146.   
  147.     public int size() {  
  148.         try {  
  149.             readLock.lock();  
  150.             return map.size();  
  151.         } finally {  
  152.             readLock.unlock();  
  153.         }  
  154.     }  
  155.   
  156.     public long getCount(K key) {  
  157.         try {  
  158.             readLock.lock();  
  159.             ValueEntry valueEntry = map.get(key);  
  160.             if (valueEntry != null) {  
  161.                 return valueEntry.count.get();  
  162.             }  
  163.             return 0;  
  164.         } finally {  
  165.             readLock.unlock();  
  166.         }  
  167.     }  
  168.   
  169.     public Collection<Entry<K, V>> getAll() {  
  170.         try {  
  171.             readLock.lock();  
  172.             Set<K> keys = map.keySet();  
  173.             Map<K, V> tmp = new HashMap<K, V>();  
  174.             for (K key : keys) {  
  175.                 tmp.put(key, map.get(key).value);  
  176.             }  
  177.             return new ArrayList<Entry<K, V>>(tmp.entrySet());  
  178.         } finally {  
  179.             readLock.unlock();  
  180.         }  
  181.     }  
  182.   
  183.     class ValueEntry implements Serializable {  
  184.   
  185.         /** 
  186.          *  
  187.          */  
  188.         private static final long serialVersionUID = -7626403101359191860L;  
  189.   
  190.         private V value;  
  191.   
  192.         private AtomicLong count;  
  193.   
  194.         private AtomicLong lastAccess;  
  195.   
  196.         public ValueEntry(V value) {  
  197.             this.value = value;  
  198.             this.count = new AtomicLong(0);  
  199.             lastAccess = new AtomicLong(System.nanoTime());  
  200.         }  
  201.   
  202.         public void updateLastAccess() {  
  203.             this.lastAccess.set(System.nanoTime());  
  204.         }  
  205.   
  206.     }  
  207. }  

 


参考:http://www.cnblogs.com/lzrabbit/p/3734850.html
0 0
原创粉丝点击