/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.util; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.AbstractMap; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * * <p> * This is similar to the standard <code>WeakHashMap</code>, but it * uses <code>SoftReference</code>s for the map's values instead * of <code>WeakReference</code>s for the maps keys. Thus, an entry is removed * from the map when the JVM is low in memory and when the entry's value is * softly reachable. * </p> * * @author Borislav Iordanov * * @param <K> The type of the map key. * @param <V> The type of the map value. */ @SuppressWarnings("unchecked") public class SoftHashMap <K, V> extends AbstractMap<K, V> { private static class SoftValue<Ka, Va> extends SoftReference<Va> { @SuppressWarnings("unused") private final Ka key; private SoftValue(Ka aKey, Va aValue, ReferenceQueue<Va> q) { super(aValue, q); this.key = aKey; } } private class E implements Map.Entry<K, SoftValue<K,V>> { private K k; private SoftValue<K,V> v; public E(K k, V v) { this.k = k; this.v = new SoftValue<K,V>(k, v, queue); } public SoftValue<K,V> getValue() { return v; } public SoftValue<K,V> setValue(SoftValue<K,V> v) { SoftValue<K,V> old = v; this.v = v; return old;} public K getKey() { return k; } } private static class F<K, V> implements Map.Entry<K, V> { private K k; private V v; public F(K k, V v) { this.k = k; this.v = v; } public V getValue() { return v; } public V setValue(V v) { V old = v; this.v = v; return old;} public K getKey() { return k; } } private final Map<K, SoftValue<K, V>> hash; private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); private void processQueue() { SoftValue<K, V> sv; while ((sv = (SoftValue<K,V>) queue.poll()) != null) { hash.remove(sv.key); } } public SoftHashMap() { hash = new HashMap<K, SoftValue<K, V>>(); } public SoftHashMap(int initialCapacity) { hash = new HashMap<K, SoftValue<K, V>>(initialCapacity); } public SoftHashMap(int initialCapacity, float loadFactor) { hash = new HashMap<K, SoftValue<K, V>>(initialCapacity, loadFactor); } public SoftHashMap(Map<? extends K,? extends V> m) { this(); for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { hash.put(e.getKey(), new SoftValue<K,V>(e.getKey(), e.getValue(), queue)); } } @Override public V get(Object key) { V result = null; SoftValue<K, V> soft_ref = hash.get(key); if (soft_ref != null) { result = soft_ref.get(); if (result == null) hash.remove(key); } return result; } public V put(K key, V value) { processQueue(); hash.put(key, new SoftValue<K,V>(key, value, queue)); return value; } public V remove(Object key) { processQueue(); SoftValue<K,V> soft = hash.remove(key); return soft == null ? null : soft.get(); } public void clear() { processQueue(); hash.clear(); } public int size() { processQueue(); return hash.size(); } public Set<Map.Entry<K, V>> entrySet() { final Set<Map.Entry<K, SoftValue<K,V>>> s = hash.entrySet(); return new Set<Map.Entry<K, V>>() { public boolean add(Map.Entry<K, V> e) { return s.add(new E(e.getKey(), e.getValue())); } public boolean addAll(Collection<? extends Map.Entry<K, V>> c) { boolean result = false; for (Map.Entry<K,V> e : c) if (this.add(e)) result = true; return result; } public void clear() { s.clear(); } public boolean contains(Object o) { if (! (o instanceof Map.Entry)) return false; else { Map.Entry<K,V> e = (Map.Entry<K,V>)o; return s.contains(new E(e.getKey(), e.getValue())); } } public boolean containsAll(Collection<?> c) { for (Object x:c) if (!this.contains(x)) return false; return true; } public boolean isEmpty() { return s.isEmpty(); } public Iterator<Map.Entry<K, V>> iterator() { final Iterator<Map.Entry<K, SoftValue<K, V>>> i = s.iterator(); return new Iterator<Map.Entry<K, V>>() { public void remove() { i.remove(); } public boolean hasNext() { return i.hasNext(); } public Map.Entry<K, V> next() { Map.Entry<K, SoftValue<K, V>> e = i.next(); return new F<K,V>(e.getKey(), e.getValue().get()); } }; } public boolean remove(Object o) { if (! (o instanceof Map.Entry)) return false; else { Map.Entry<K,V> e = (Map.Entry<K,V>)o; return s.remove(new E(e.getKey(), e.getValue())); } } public boolean removeAll(Collection c) { boolean result = false; for (Object x:c) if (this.remove(x)) result = true; return result; } public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } public int size() { return s.size(); } public Object[] toArray() { return toArray(null); } public Object[] toArray(Object[] a) { F [] result = null; if (a != null && a instanceof F[] && a.length >= size()) result = (F[])a; else result = new F[size()]; Object [] A = s.toArray(); for (int i = 0; i < A.length; i++) { Map.Entry<K, SoftValue<K,V>> e = (Map.Entry<K, SoftValue<K,V>>)A[i]; result[i] = new F<K,V>(e.getKey(), e.getValue().get()); } return result; } }; } }