bboss.org.jgroups.blocks.LazyRemovalCache Maven / Gradle / Ivy
The newest version!
package bboss.org.jgroups.blocks;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Cache which doesn't remove elements on remove(), removeAll() or retainAll(), but only removes elements when a
* configurable size limit has been exceeded. In that case, all elements marked as removable and older than a
* configurable time are evicted. Elements are marked as removable by remove(), removeAll() and retainAll(). When
* an elements is marked as removable, but later reinserted, the mark is removed.
* @author Bela Ban
* @version $Id: LazyRemovalCache.java,v 1.6 2010/04/22 12:13:06 belaban Exp $
*/
public class LazyRemovalCache {
private final ConcurrentMap> map=new ConcurrentHashMap>();
/** Max number of elements, if exceeded, we remove all elements marked as removable and older than max_age ms */
private final int max_elements;
private final long max_age;
public interface Printable {
String print(K key,V val);
}
public LazyRemovalCache() {
this(200, 5000L);
}
public LazyRemovalCache(int max_elements, long max_age) {
this.max_elements=max_elements;
this.max_age=max_age;
}
public void add(K key, V val) {
if(key != null && val != null)
map.put(key, new Entry(val)); // overwrite existing element (new timestamp, and possible removable mark erased)
checkMaxSizeExceeded();
}
public V get(K key) {
if(key == null)
return null;
Entry entry=map.get(key);
return entry != null? entry.val : null;
}
public K getByValue(V val) {
if(val == null) return null;
for(Map.Entry> entry: map.entrySet()) {
Entry v=entry.getValue();
if(v.val != null && v.val.equals(val))
return entry.getKey();
}
return null;
}
public void remove(K key) {
remove(key, false);
}
public void remove(K key, boolean force) {
if(key == null)
return;
if(force)
map.remove(key);
else {
Entry entry=map.get(key);
if(entry != null)
entry.removable=true;
}
checkMaxSizeExceeded();
}
public void removeAll(Collection keys) {
removeAll(keys, false);
}
public void removeAll(Collection keys, boolean force) {
if(keys == null || keys.isEmpty())
return;
if(force)
map.keySet().removeAll(keys);
else {
for(K key: keys) {
Entry entry=map.get(key);
if(entry != null)
entry.removable=true;
}
}
checkMaxSizeExceeded();
}
public void clear(boolean force) {
if(force)
map.clear();
else {
for(Map.Entry> entry: map.entrySet()) {
Entry val=entry.getValue();
if(val != null) {
Entry tmp=entry.getValue();
if(tmp != null)
tmp.removable=true;
}
}
}
}
public void retainAll(Collection keys) {
retainAll(keys, false);
}
public void retainAll(Collection keys, boolean force) {
if(keys == null || keys.isEmpty())
return;
if(force)
map.keySet().retainAll(keys);
else {
for(Map.Entry> entry: map.entrySet()) {
if(!keys.contains(entry.getKey())) {
Entry val=entry.getValue();
if(val != null)
val.removable=true;
}
}
}
// now make sure that all elements in keys have removable=false
for(K key: keys) {
Entry val=map.get(key);
if(val != null && val.removable)
val.removable=false;
}
checkMaxSizeExceeded();
}
public Set values() {
Set retval=new HashSet();
for(Entry entry: map.values()) {
retval.add(entry.val);
}
return retval;
}
public Mapcontents() {
Map retval=new HashMap();
for(Map.Entry> entry: map.entrySet())
retval.put(entry.getKey(), entry.getValue().val);
return retval;
}
public int size() {
return map.size();
}
public String printCache() {
StringBuilder sb=new StringBuilder();
for(Map.Entry> entry: map.entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
return sb.toString();
}
public String printCache(Printable print_function) {
StringBuilder sb=new StringBuilder();
for(Map.Entry> entry: map.entrySet()) {
K key=entry.getKey();
V val=entry.getValue().val;
sb.append(print_function.print(key, val));
}
return sb.toString();
}
public String toString() {
return printCache();
}
private void checkMaxSizeExceeded() {
if(map.size() > max_elements) {
removeMarkedElements();
}
}
/**
* Removes elements marked as removable
* @param force If set to true, all elements marked as 'removable' will get removed, regardless of expiration
*/
public void removeMarkedElements(boolean force) {
long curr_time=System.currentTimeMillis();
for(Iterator>> it=map.entrySet().iterator(); it.hasNext();) {
Map.Entry> entry=it.next();
Entry tmp=entry.getValue();
if(tmp == null)
continue;
if(tmp.removable && (curr_time - tmp.timestamp) >= max_age || force) {
it.remove();
}
}
}
/**
* Removes elements marked as removable
*/
public void removeMarkedElements() {
removeMarkedElements(false);
}
private static class Entry {
private final V val;
private final long timestamp=System.currentTimeMillis();
private boolean removable=false;
public Entry(V val) {
this.val=val;
}
public String toString() {
return val + " (" + (System.currentTimeMillis() - timestamp) + "ms old" + (removable? ", removable" : "") + ")";
}
}
}