Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java time-based map/cache with expiring keys [closed]

Do any of you know of a Java Map or similar standard data store that automatically purges entries after a given timeout? This means aging, where the old expired entries “age-out” automatically.

Preferably in an open source library that is accessible via Maven?

I know of ways to implement the functionality myself and have done it several times in the past, so I'm not asking for advice in that respect, but for pointers to a good reference implementation.

WeakReference based solutions like WeakHashMap are not an option, because my keys are likely to be non-interned strings and I want a configurable timeout that's not dependent on the garbage collector.

Ehcache is also an option I wouldn't like to rely on because it needs external configuration files. I am looking for a code-only solution.

like image 926
Sean Patrick Floyd Avatar asked Sep 27 '10 09:09

Sean Patrick Floyd


People also ask

What is cache expire time?

The value of the Expires date/time can cause the following specific cache behavior: When the Expires date is equal to the Date header value, the response is considered to be expired. When a response has an Expires header field with a date/time that is in the future, then that response is considered "cacheable".

What is JCache?

JCache is a de facto standard Java cache API for caching data. Also known as JSR 107 (i.e., a “Java Specification Request” from the “Java Community Process” [JCP]), this API implementation is intended to create a way for different technologies to provide a common caching interface.

What is ehcache used for?

Ehcache is a standards-based caching API that is used by Integration Server. Caching enables an application to fetch frequently used data from memory (or other nearby resource) rather than having to retrieve it from a database or other back-end system each time the data is needed.


2 Answers

Yes. Google Collections, or Guava as it is named now has something called MapMaker which can do exactly that.

ConcurrentMap<Key, Graph> graphs = new MapMaker()    .concurrencyLevel(4)    .softKeys()    .weakValues()    .maximumSize(10000)    .expiration(10, TimeUnit.MINUTES)    .makeComputingMap(        new Function<Key, Graph>() {          public Graph apply(Key key) {            return createExpensiveGraph(key);          }        }); 

Update:

As of guava 10.0 (released September 28, 2011) many of these MapMaker methods have been deprecated in favour of the new CacheBuilder:

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()     .maximumSize(10000)     .expireAfterWrite(10, TimeUnit.MINUTES)     .build(         new CacheLoader<Key, Graph>() {           public Graph load(Key key) throws AnyException {             return createExpensiveGraph(key);           }         }); 
like image 194
Shervin Asgari Avatar answered Oct 01 '22 21:10

Shervin Asgari


This is a sample implementation that i did for the same requirement and concurrency works well. Might be useful for someone.

import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap;  /**  *   * @author Vivekananthan M  *  * @param <K>  * @param <V>  */ public class WeakConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {      private static final long serialVersionUID = 1L;      private Map<K, Long> timeMap = new ConcurrentHashMap<K, Long>();     private long expiryInMillis = 1000;     private static final SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss:SSS");      public WeakConcurrentHashMap() {         initialize();     }      public WeakConcurrentHashMap(long expiryInMillis) {         this.expiryInMillis = expiryInMillis;         initialize();     }      void initialize() {         new CleanerThread().start();     }      @Override     public V put(K key, V value) {         Date date = new Date();         timeMap.put(key, date.getTime());         System.out.println("Inserting : " + sdf.format(date) + " : " + key + " : " + value);         V returnVal = super.put(key, value);         return returnVal;     }      @Override     public void putAll(Map<? extends K, ? extends V> m) {         for (K key : m.keySet()) {             put(key, m.get(key));         }     }      @Override     public V putIfAbsent(K key, V value) {         if (!containsKey(key))             return put(key, value);         else             return get(key);     }      class CleanerThread extends Thread {         @Override         public void run() {             System.out.println("Initiating Cleaner Thread..");             while (true) {                 cleanMap();                 try {                     Thread.sleep(expiryInMillis / 2);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }          private void cleanMap() {             long currentTime = new Date().getTime();             for (K key : timeMap.keySet()) {                 if (currentTime > (timeMap.get(key) + expiryInMillis)) {                     V value = remove(key);                     timeMap.remove(key);                     System.out.println("Removing : " + sdf.format(new Date()) + " : " + key + " : " + value);                 }             }         }     } } 


Git Repo Link (With Listener Implementation)

https://github.com/vivekjustthink/WeakConcurrentHashMap

Cheers!!

like image 34
Vivek Avatar answered Oct 01 '22 21:10

Vivek