Implemented a map that maintains soft references to its values so that they may

be garbage collected. If you iterate over the keys, values or entries, you have
to cope with interleaved null values, doing the "right" thing there is a bit
PITA and iterating over the cache is wacky anyway because size() will most
likely never be right (unless we validated every entry in the cache at the time
you called size() which is also wacky).


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1885 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2006-08-10 23:05:45 +00:00
parent 4fcc4a60eb
commit cc1f0b34ab
2 changed files with 259 additions and 0 deletions
@@ -0,0 +1,90 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.util.Map;
/**
* A useful building block for implementing one's own {@link Map} classes. Sun
* has this same damned code in AbstractMap and have idiotically declared it
* package protected, with a genius comment saying "This should be made public
* as soon as possible. It greatly simplifies the task of implementing Map." No
* doubt that comment was added half a decade ago. Thanks guys!
*/
public class MapEntry<K,V> implements Map.Entry<K,V>
{
public MapEntry (K key, V value)
{
_key = key;
_value = value;
}
public MapEntry (Map.Entry<K,V> e)
{
_key = e.getKey();
_value = e.getValue();
}
// from interface Map.Entry
public K getKey ()
{
return _key;
}
// from interface Map.Entry
public V getValue ()
{
return _value;
}
// from interface Map.Entry
public V setValue (V value)
{
V oldValue = _value;
_value = value;
return oldValue;
}
@Override // from Object
public boolean equals (Object o)
{
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry e = (Map.Entry)o;
return ObjectUtil.equals(_key, e.getKey()) &&
ObjectUtil.equals(_value, e.getValue());
}
@Override // from Object
public int hashCode ()
{
return ((_key == null) ? 0 : _key.hashCode()) ^
((_value == null) ? 0 : _value.hashCode());
}
@Override // from Object
public String toString ()
{
return _key + "=" + _value;
}
protected K _key;
protected V _value;
}
@@ -0,0 +1,169 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2006 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.lang.ref.SoftReference;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Implements a {@link SoftReference} cache wherein the values in the hashmap
* are not prevented from being garbage collected.
*
* <p><em>Beware!</em> if you iterate over the contents of this map, some
* values may be null. Also note that if you try to store null values in this
* map, those entries will be pruned because the map cannot distinguish between
* an expired soft reference and a null value. Why would you cache null anyway?
*/
public class SoftCacheMap<K,V> extends AbstractMap<K,V>
{
public SoftCacheMap (int initialCapacity, float loadFactor)
{
_map = new HashMap<K,SoftReference<V>>(initialCapacity, loadFactor);
}
public SoftCacheMap (int initialCapacity)
{
_map = new HashMap<K,SoftReference<V>>(initialCapacity);
}
public SoftCacheMap ()
{
_map = new HashMap<K,SoftReference<V>>();
}
// from interface Map
public int size ()
{
return _map.size();
}
// from interface Map
public boolean containsKey (Object key)
{
return (get(key) != null);
}
// from interface Map
public V get (Object key)
{
V value = null;
SoftReference<V> ref = _map.get(key);
if (ref != null) {
value = ref.get();
if (value == null) {
_map.remove(key);
}
}
return value;
}
// from interface Map
public V put (K key, V value)
{
SoftReference<V> old = _map.put(key, new SoftReference<V>(value));
return (old == null) ? null : old.get();
}
// from interface Map
public V remove (Object key)
{
SoftReference<V> ref = _map.remove(key);
return (ref == null) ? null : ref.get();
}
// from interface Map
public void clear ()
{
_map.clear();
}
// from interface Map
public Set<K> keySet ()
{
return _map.keySet();
}
// from interface Map
public Set<Entry<K,V>> entrySet ()
{
return new EntrySet();
}
/**
* Used by {@link EntrySet}.
*/
protected Entry<K,V> getEntry (Object key)
{
SoftReference<V> value = _map.get(key);
@SuppressWarnings("unchecked") K ckey = (K)key;
return (value == null) ? null : new MapEntry<K,V>(ckey, value.get());
}
protected class EntrySet extends AbstractSet<Entry<K,V>> {
public Iterator<Entry<K,V>> iterator () {
final Iterator<Entry<K,SoftReference<V>>> iter =
_map.entrySet().iterator();
return new Iterator<Entry<K,V>>() {
public boolean hasNext () {
return iter.hasNext();
}
public Entry<K,V> next () {
Entry<K,SoftReference<V>> ent = iter.next();
return (ent == null) ? null :
new MapEntry<K,V>(ent.getKey(), ent.getValue().get());
}
public void remove () {
iter.remove();
}
};
}
public boolean contains (Object o) {
if (!(o instanceof Entry)) {
return false;
}
@SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)o;
return containsKey(entry.getKey());
}
public boolean remove (Object o) {
if (!(o instanceof Entry)) {
return false;
}
@SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)o;
return remove(entry.getKey());
}
public int size () {
return _map.size();
}
public void clear () {
_map.clear();
}
}
protected HashMap<K,SoftReference<V>> _map;
}