com.venky.cache.Cache Maven / Gradle / Ivy
package com.venky.cache;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import com.venky.core.checkpoint.Mergeable;
import com.venky.core.util.Bucket;
import com.venky.core.util.ObjectUtil;
public abstract class Cache implements Mergeable> , Serializable{
/**
*
*/
private static final long serialVersionUID = -4801418262910565684L;
public static final int MAX_ENTRIES_DEFAULT = 1000;
public static final int MAX_ENTRIES_UNLIMITED = 0;
public static final double PRUNE_FACTOR_DEFAULT = 0.8;
private Bucket fakeTime = new Bucket();
protected Cache(){
this(MAX_ENTRIES_DEFAULT,PRUNE_FACTOR_DEFAULT);
}
private final int maxEntries ;
private final double pruneFactor ;
protected Cache(int maxEntries,double pruneFactor){
this.maxEntries = maxEntries;
this.pruneFactor = pruneFactor;
if (this.pruneFactor > 1 || this.pruneFactor < 0 ){
throw new IllegalArgumentException("Prune factor must be between 0.0 than 1.0");
}
}
public void makeSpace(){
if (maxEntries != MAX_ENTRIES_UNLIMITED && size() >= maxEntries){
int numEntriesToRemove = (int)(size() * pruneFactor);
if (numEntriesToRemove <= 0){
return;
}
if (pruneFactor == 1){
cacheMap.clear();
accessTimeMap.clear();
return;
}
SortedMap> keysAccessedByTime = new TreeMap>();
for (K key : accessTimeMap.keySet()){
Long time = accessTimeMap.get(key);
List keys = keysAccessedByTime.get(time);
if (keys == null){
keys = new ArrayList();
keysAccessedByTime.put(time, keys);
}
keys.add(key);
}
int numEntriesRemoved = 0;
for (Long time: keysAccessedByTime.keySet()){//We will read in the order of being Accessed.
for (K key : keysAccessedByTime.get(time)){
cacheMap.remove(key);
accessTimeMap.remove(key);
numEntriesRemoved ++;
}
if (numEntriesRemoved >= numEntriesToRemove){
break;
}
}
}
}
@SuppressWarnings("unchecked")
public Cache clone(){
try {
Cache clone = (Cache)super.clone();
clone.accessTimeMap = (HashMap)accessTimeMap.clone();
clone.cacheMap = (HashMap)cacheMap.clone();
for (K k :clone.cacheMap.keySet()){
clone.cacheMap.put(k, ObjectUtil.clone(clone.get(k)));
}
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void merge(Cache another){
ObjectUtil.mergeValues(another.accessTimeMap,this.accessTimeMap);
ObjectUtil.mergeValues(another.cacheMap,this.cacheMap);
}
public int size(){
return cacheMap.size();
}
public Set keySet(){
return cacheMap.keySet();
}
private HashMap cacheMap = new HashMap();
private HashMap accessTimeMap = new HashMap();
public V get(K key){
V v = cacheMap.get(key);
if (v == null && !cacheMap.containsKey(key)){
synchronized (cacheMap) {
v = cacheMap.get(key);
if (v == null && !cacheMap.containsKey(key)){
makeSpace();
v = getValue(key);
cacheMap.put(key, v);
}
}
}
fakeTime.increment();
accessTimeMap.put(key, fakeTime.longValue());
return v;
}
public void remove(K key){
synchronized (cacheMap) {
cacheMap.remove(key);
accessTimeMap.remove(key);
}
}
public void clear(){
synchronized (cacheMap) {
cacheMap.clear();
accessTimeMap.clear();
}
}
public void put(K key,V value){
synchronized (cacheMap) {
cacheMap.put(key, value);
fakeTime.increment();
accessTimeMap.put(key, fakeTime.longValue());
}
}
protected abstract V getValue(K k);
public Long accessTime(K k){
return accessTimeMap.get(k);
}
public Collection values(){
return cacheMap.values();
}
}