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.
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".
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.
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.
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); } });
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!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With