I'm implementing a cache for Objects stored persistently. The idea is:
getObjectFromPersistence(long id); ///Takes about 3 seconds
getObjectFromCache(long id) //Instantly
And have a method: getObject(long id)
with the following pseudocode:
synchronized(this){
CustomObject result= getObjectFromCache(id)
if (result==null){
result=getObjectFromPersistence(id);
addToCache(result);
}
return result;
}
But I need to allow the CustomObject to be collected by the garbage collector. Until now I was using an HashMap<Long,WeakReference<CustomObject>
for the implementation. The problem is that over the time the HashMap becomes filled of empty WeakReferences
.
I've checked WeakHashMap but there the keys are weak (and the values are still strong references) so having the longs with WeakReferences have no sense.
Whats the best solution for solving this problem? Is there some "inverse WeakHashMap" or something similar?
Thanks
You can use the Guava MapMaker
for this:
ConcurrentMap<Long, CustomObject> graphs = new MapMaker()
.weakValues()
.makeMap();
You can even include the computation part by replacing makeMap()
with this:
.makeComputingMap(
new Function<Long, CustomObject>() {
public CustomObject apply(Long id) {
return getObjectFromPersistence(id);
}
});
Since what you are writing looks a lot like a cache, the newer, more specialized Cache
(built via a CacheBuilder
) might be even more relevant to you. It doesn't implement the Map
interface directly, but provides even more controls that you might want for a cache.
You can refer to this for a detailed how to work for CacheBuilder and here is an example for fast access:
LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Integer, String>() {
@Override
public String load(Integer id) throws Exception {
return "value";
}
}
);