/*
* Copyright 2011 Stefan Partusch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.spartusch;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Simple implementation of a cache using {@link java.lang.ref.SoftReference
* SoftReferences}. This class is thread-safe.
* @author Stefan Partusch
*
* @param <K> Type of keys maintained by this cache
* @param <V> Type of mapped values
*/
public class SoftCache<K, V> {
/**
* A class for values in the cache. The cache is using a {@link
* java.util.WeakHashMap WeakHashMap} internally, which uses {@link
* java.lang.ref.WeakReference WeakReferences} to store its keys. In order
* to prevent the WeakHashMap from removing the entries immediately this
* class keeps a strong reference to the key of each value.
* @author Stefan Partusch
*/
private static class KeyValue<K, V> {
/** Strong reference to the value's key */
private K key;
/** The actual value */
private V value;
/**
* Creates a new value for the cache with a strong reference
* to its key.
* @param key Key of the value
* @param value The value to store
*/
public KeyValue(final K key, final V value) {
this.key = key;
this.value = value;
}
/**
* Returns the key of the value.
* @return Key of the value
*/
@SuppressWarnings("unused")
public K getKey() {
return key;
}
/**
* Returns the value.
* @return The value
*/
public V getValue() {
return value;
}
}
/** The synchronized WeakHashMap with the SoftReferences */
private Map<K, SoftReference<KeyValue<K, V>>> map;
/**
* Creates a new and empty SoftCache.
*/
public SoftCache() {
map = Collections.synchronizedMap(
new WeakHashMap<K, SoftReference<KeyValue<K, V>>>());
}
/**
* Stores the specified value with the specified key in this cache.
* If the cache previously contained a mapping for this key,
* the old value is replaced.
* @param key Key of the value
* @param value The value to store
* @return The previous value stored using key, or null if there
* was no such previous value.
*/
public V put(K key, V value) {
KeyValue<K, V> newKV = new KeyValue<K, V>(key, value);
SoftReference<KeyValue<K, V>> newRef =
new SoftReference<KeyValue<K,V>>(newKV);
SoftReference<KeyValue<K, V>> oldRef = map.put(key, newRef);
if (oldRef != null) {
KeyValue<K, V> oldKV = oldRef.get();
if (oldKV != null) {
return oldKV.getValue();
}
}
return null;
}
/**
* Returns the value mapped to with key, or null
* if this cache contains no mapping for the key.
* @param key Key of the value to return
* @return The stored value
*/
public V get(K key) {
SoftReference<KeyValue<K, V>> ref = map.get(key);
if (ref != null) {
KeyValue<K, V> kv = ref.get();
if (kv != null) {
return kv.getValue();
}
}
return null;
}
}