![JAR search and dependency download from the Maven repository](/logo.png)
de.tsl2.nano.collection.ExpiringMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tsl2.nano.datastructure Show documentation
Show all versions of tsl2.nano.datastructure Show documentation
optimized implementations for trees, collections, arrays, historized input
The newest version!
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Tom
* created on: 24.02.2015
*
* Copyright: (c) Thomas Schneider 2015, all rights reserved
*/
package de.tsl2.nano.collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import de.tsl2.nano.core.cls.PrivateAccessor;
import de.tsl2.nano.core.log.LogFactory;
/**
* map holding entries with a lifecycle defined by a {@link #timeout}. on adding elements, old elements may be removed.
* useful for in-memory caches. if you define a timeout of -1, the expiration functionality will be switched off.
*
* Note-1: the {@link LinkedHashMap} implementation provides an accessOrder (see it's constructors) and an unimplemented
* {@link #removeEldestEntry(java.util.Map.Entry)} - these are not usable for our cache.
*
* Note-2: As the capacity() evaluation is only be accessible through pure-performance reflection, we may use our
* timeout variable as {@link #shrink()}ing frequency.
*
* @author Tom
* @version $Revision$
*/
public class ExpiringMap extends LinkedHashMap {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
private static final Log LOG = LogFactory.getLog(ExpiringMap.class);
/** holding touch-times of all entries */
Map touches;
/** maps current capacity (as it is not visible from here, we get it through reflection!) */
int capacity;
/** life cycle of elements */
long timeout;
/**
* constructor
*
* @param timeout
*/
public ExpiringMap(long timeout) {
super();
init(timeout);
}
/**
* constructor
*
* @param initialCapacity
* @param loadFactor
* @param accessOrder
*/
public ExpiringMap(int initialCapacity, float loadFactor, boolean accessOrder, long timeout) {
super(initialCapacity, loadFactor, accessOrder);
init(timeout);
}
/**
* constructor
*
* @param initialCapacity
* @param loadFactor
*/
public ExpiringMap(int initialCapacity, float loadFactor, long timeout) {
super(initialCapacity, loadFactor);
init(timeout);
}
/**
* constructor
*
* @param initialCapacity
*/
public ExpiringMap(int initialCapacity, long timeout) {
super(initialCapacity);
init(timeout);
}
/**
* constructor
*
* @param m
*/
public ExpiringMap(Map extends K, ? extends V> m, long timeout) {
super(m);
init(timeout);
}
@SuppressWarnings("unchecked")
@Override
public V get(Object key) {
if (timeout != -1) {
Long access = touches.get(key);
if (access != null && System.currentTimeMillis() - access > timeout) {
remove(key);
return null;
}
touch((K) key);
}
return super.get(key);
}
private void touch(K key) {
touches.put(key, System.currentTimeMillis());
}
@Override
public V put(K key, V value) {
if (timeout != -1) {
touch(key);
shrink();
}
return super.put(key, value);
}
@Override
public V remove(Object key) {
if (timeout != -1) {
touches.remove(key);
}
return super.remove(key);
}
@Override
public void clear() {
if (timeout != -1) {
touches.clear();
}
super.clear();
}
/**
* does the thing and removes expired objects.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void shrink() {
if (size() >= capacity) {
long now = System.currentTimeMillis();
Set> entries = entrySet();
for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
java.util.Map.Entry entry = (java.util.Map.Entry) iterator.next();
if (now - touches.get(entry.getKey()) > timeout) {
LOG.info("removing expired entry " + entry);
iterator.remove();
}
}
refreshCapacity();
}
}
/**
* creates the touch-time table
*
* @param timeout The timeout to set.
*/
protected void init(long timeout) {
if (timeout != -1) {
touches = new HashMap();
Set ks = keySet();
long now = System.currentTimeMillis();
for (K k : ks) {
touches.put(k, now);
}
refreshCapacity();
}
this.timeout = timeout;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
void refreshCapacity() {
// WARN: the capacity() method from Hashtable is implementation specific. other VMs may not have that method
try {
capacity = (Integer) new PrivateAccessor(this).call("capacity", Integer.class);
} catch(Exception ex) {
LOG.warn(ex.toString());
capacity = size();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy