package com.orientechnologies.orient.core.cache;
import com.orientechnologies.common.log.OLogManager;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Soft References Map inspired by the code published by Dr. Heinz M. Kabutz on http://www.javaspecialists.eu/archive/Issue015.html.
*/
public class OSoftRefsHashMap<K, V> extends AbstractMap<K, V> implements Serializable {
private final Map<K, SoftReference<V>> hashCodes = new ConcurrentHashMap<K, SoftReference<V>>();
private final Map<SoftReference<V>, K> reverseLookup = new ConcurrentHashMap<SoftReference<V>, K>();
private final ReferenceQueue<V> refQueue = new ReferenceQueue<V>();
public V get(Object key) {
evictStaleEntries();
V result = null;
final SoftReference<V> soft_ref = hashCodes.get(key);
if (soft_ref != null) {
result = soft_ref.get();
if (result == null) {
hashCodes.remove(key);
reverseLookup.remove(soft_ref);
}
}
return result;
}
private void evictStaleEntries() {
int evicted = 0;
Reference<? extends V> sv;
while ((sv = refQueue.poll()) != null) {
final K key = reverseLookup.remove(sv);
if (key != null) {
hashCodes.remove(key);
evicted++;
}
}
if (evicted > 0)
OLogManager.instance().debug(this, "Evicted %d items", evicted);
}
public V put(final K key, final V value) {
evictStaleEntries();
final SoftReference<V> soft_ref = new SoftReference<V>(value, refQueue);
reverseLookup.put(soft_ref, key);
final SoftReference<V> result = hashCodes.put(key, soft_ref);
if (result == null)
return null;
reverseLookup.remove(result);
return result.get();
}
public V remove(Object key) {
evictStaleEntries();
final SoftReference<V> result = hashCodes.remove(key);
if (result == null)
return null;
return result.get();
}
public void clear() {
hashCodes.clear();
reverseLookup.clear();
}
public int size() {
evictStaleEntries();
return hashCodes.size();
}
public Set<Entry<K, V>> entrySet() {
evictStaleEntries();
Set<Entry<K, V>> result = new LinkedHashSet<Entry<K, V>>();
for (final Entry<K, SoftReference<V>> entry : hashCodes.entrySet()) {
final V value = entry.getValue().get();
if (value != null) {
result.add(new Entry<K, V>() {
public K getKey() {
return entry.getKey();
}
public V getValue() {
return value;
}
public V setValue(V v) {
entry.setValue(new SoftReference<V>(v, refQueue));
return value;
}
});
}
}
return result;
}
}