diff --git a/src/com/silverwrist/util/cache/CacheMap.java b/src/com/silverwrist/util/cache/CacheMap.java
index 69e0633..4e97724 100644
--- a/src/com/silverwrist/util/cache/CacheMap.java
+++ b/src/com/silverwrist/util/cache/CacheMap.java
@@ -20,6 +20,17 @@ package com.silverwrist.util.cache;
import java.lang.ref.*;
import java.util.*;
+/**
+ * A special kind of Map
that acts as a cache of data items. The CacheMap
+ * uses SoftReferences
, so its data values will be shed if the virtual machine needs
+ * more memory; also, if too many entries are added to the map, certain entries will be removed in
+ * accordance with the supplied or default CacheMapStrategy
object.
+ *
+ * @author Eric J. Bowersox <erbo@silcom.com>
+ * @version X
+ * @see CacheMapStrategy
+ * @see java.util.Map
+ */
public class CacheMap implements Map
{
/*--------------------------------------------------------------------------------
@@ -27,7 +38,7 @@ public class CacheMap implements Map
*--------------------------------------------------------------------------------
*/
- static class CacheOrdering implements Comparator
+ static final class CacheOrdering implements Comparator
{
private CacheMapStrategy strategy; // CacheMap's strategy object
private long tick; // when the sort operation started
@@ -60,7 +71,7 @@ public class CacheMap implements Map
*--------------------------------------------------------------------------------
*/
- static class DefaultStrategy implements CacheMapStrategy
+ static final class DefaultStrategy implements CacheMapStrategy
{
private static final long SCALING_FACTOR = 5000;
@@ -100,6 +111,17 @@ public class CacheMap implements Map
*--------------------------------------------------------------------------------
*/
+ /**
+ * Constructs a new CacheMap
.
+ *
+ * @param capacity The maximum number of entries this map can contain.
+ * @param shrink_percentage The percentage of entries which will be removed from the cache
+ * whenever it needs to shrink itself.
+ * @param strategy The strategy object which is used to determine which elements to remove.
+ * @exception java.lang.IllegalArgumentException If the specified capacity is negative or zero, or
+ * the shrink percentage is not in the range [1..100].
+ * @exception java.lang.NullPointerException If the strategy object reference is null
.
+ */
public CacheMap(int capacity, int shrink_percentage, CacheMapStrategy strategy)
{
if (capacity<=0)
@@ -118,12 +140,27 @@ public class CacheMap implements Map
} // end constructor
+ /**
+ * Constructs a new CacheMap
with a default strategy.
+ *
+ * @param capacity The maximum number of entries this map can contain.
+ * @param shrink_percentage The percentage of entries which will be removed from the cache
+ * whenever it needs to shrink itself.
+ * @exception java.lang.IllegalArgumentException If the specified capacity is negative or zero, or
+ * the shrink percentage is not in the range [1..100].
+ */
public CacheMap(int capacity, int shrink_percentage)
{
this(capacity,shrink_percentage,default_strategy_singleton);
} // end constructor
+ /**
+ * Constructs a new CacheMap
with a default strategy and shrink percentage.
+ *
+ * @param capacity The maximum number of entries this map can contain.
+ * @exception java.lang.IllegalArgumentException If the specified capacity is negative or zero.
+ */
public CacheMap(int capacity)
{
this(capacity,10,default_strategy_singleton);
@@ -137,9 +174,9 @@ public class CacheMap implements Map
private void doSweep()
{
- Reference r = rq.poll();
- ArrayList ditch = new ArrayList();
- Iterator it;
+ Reference r = rq.poll(); // the reference that's been cleared
+ ArrayList ditch = new ArrayList(); // a list of entries to ditch
+ Iterator it; // current iterator
while (r!=null)
{ // look for the dead reference in the element list
@@ -195,6 +232,12 @@ public class CacheMap implements Map
*--------------------------------------------------------------------------------
*/
+ /**
+ * Returns the number of key-value mappings in this map. If the map contains more than
+ * Integer.MAX_VALUE
elements, returns Integer.MAX_VALUE
.
+ *
+ * @return The number of key-value mappings in this map.
+ */
public int size()
{
doSweep();
@@ -202,6 +245,11 @@ public class CacheMap implements Map
} // end size
+ /**
+ * Returns true
if this map contains no key-value mappings.
+ *
+ * @return true
if this map contains no key-value mappings.
+ */
public boolean isEmpty()
{
doSweep();
@@ -209,6 +257,14 @@ public class CacheMap implements Map
} // end isEmpty
+ /**
+ * Returns true
if this map contains a mapping for the specified key.
+ *
+ * @param key Key whose presence in this map is to be tested.
+ * @return true
if this map contains a mapping for the specified key.
+ * @exception java.lang.ClassCastException If the key is of an inappropriate type for this map.
+ * @exception java.lang.NullPointerException If the key is null
.
+ */
public boolean containsKey(Object key)
{
doSweep();
@@ -216,6 +272,12 @@ public class CacheMap implements Map
} // end containsKey
+ /**
+ * Returns true
if this map maps one or more keys to the specified value.
+ *
+ * @param value Value whose presence in this map is to be tested.
+ * @return true
if this map maps one or more keys to the specified value.
+ */
public boolean containsValue(Object value)
{
doSweep();
@@ -243,6 +305,17 @@ public class CacheMap implements Map
} // end containsValue
+ /**
+ * Returns the value to which this map maps the specified key. Returns null
if the map
+ * contains no mapping for this key.
+ *
+ * @param key Key whose associated value is to be returned.
+ * @return The value to which this map maps the specified key, or null
if the map contains
+ * no mapping for this key.
+ * @exception java.lang.ClassCastException If the key is of an inappropriate type for this map.
+ * @exception java.lang.NullPointerException If the key is null
.
+ * @see #containsKey(java.lang.Object)
+ */
public Object get(Object key)
{
doSweep();
@@ -254,6 +327,20 @@ public class CacheMap implements Map
} // end get
+ /**
+ * Associates the specified value with the specified key in this map. If the map previously contained
+ * a mapping for this key, the old value is replaced.
+ *
+ * @param key Key with which the specified value is to be associated.
+ * @param value Value to be associated with the specified key.
+ * @return The previous value associated with the specified key, or null
if there was no
+ * mapping for the key.
+ * @exception java.lang.ClassCastException If the class of the specified key or value prevents it from
+ * being stored in this map.
+ * @exception java.lang.IllegalArgumentException If some aspect of this key or value prevents it from
+ * being stored in this map.
+ * @exception java.lang.NullPointerException If the specified key or value is null
.
+ */
public Object put(Object key, Object value)
{
doSweep();
@@ -284,6 +371,13 @@ public class CacheMap implements Map
} // end put
+ /**
+ * Removes the mapping for this key from this map if present.
+ *
+ * @param key Key whose mapping is to be removed from the map.
+ * @return The previous value associated with the specified key, or null
if there was no
+ * mapping for the key.
+ */
public Object remove(Object key)
{
doSweep();
@@ -308,6 +402,17 @@ public class CacheMap implements Map
} // end remove
+ /**
+ * Copies all of the mappings from the specified map to this map. These mappings will replace any mappings
+ * that this map had for any of the keys currently in the specified map.
+ *
+ * @param map Mappings to be stored in this map.
+ * @exception java.lang.ClassCastException If the class of a key or value in the specified map prevents
+ * it from being stored in this map.
+ * @exception java.lang.IllegalArgumentException If some aspect of a key or value in the specified map
+ * prevents it from being stored in this map.
+ * @exception java.lang.NullPointerException If the specified key or value is null
.
+ */
public void putAll(Map map)
{
doSweep();
@@ -329,6 +434,9 @@ public class CacheMap implements Map
} // end putAll
+ /**
+ * Removes all mappings from this map.
+ */
public synchronized void clear()
{
base_map.clear();
@@ -344,24 +452,48 @@ public class CacheMap implements Map
} // end clear
+ /**
+ * Returns a set view of the keys contained in this map.
+ *
+ * @return A set view of the keys contained in this map.
+ */
public Set keySet()
{
return base_map.keySet();
} // end keySet
+ /**
+ * Returns a collection view of the values contained in this map.
+ *
+ * @return A collection view of the values contained in this map.
+ * @exception java.lang.UnsupportedOperationException This map does not support this operation.
+ */
public Collection values()
{
throw new UnsupportedOperationException("CacheMap.values() is not implemented");
} // end values
+ /**
+ * Returns a set view of the mappings contained in this map.
+ *
+ * @return A set view of the mappings contained in this map.
+ * @exception java.lang.UnsupportedOperationException This map does not support this operation.
+ */
public Set entrySet()
{
throw new UnsupportedOperationException("CacheMap.entrySet() is not implemented");
} // end entrySet
+ /**
+ * Compares the specified object with this map for equality. Returns true
if the given object
+ * is also a map and the two Maps represent the same mappings.
+ *
+ * @param o Object to be compared for equality with this map.
+ * @return true
if the specified object is equal to this map.
+ */
public boolean equals(Object o)
{
if ((o==null) || !(o instanceof Map))
@@ -395,6 +527,14 @@ public class CacheMap implements Map
} // end equals
+ /**
+ * Returns the hash code value for this map. The hash code of a map is defined to be the sum of the
+ * hash codes of each entry in the map's entrySet view.
+ *
+ * @return The hash code value for this map.
+ * @see #equals(java.lang.Object)
+ * @see CacheMapEntry#hashCode()
+ */
public int hashCode()
{
doSweep();
@@ -416,12 +556,23 @@ public class CacheMap implements Map
*--------------------------------------------------------------------------------
*/
+ /**
+ * Returns the capacity of this cache map.
+ *
+ * @return The capacity of this cache map.
+ */
public int getCapacity()
{
return capacity;
} // end getCapacity
+ /**
+ * Sets the capacity of this cache map.
+ *
+ * @param c The new capacity for this cache map.
+ * @exception java.lang.IllegalArgumentException If the specified capacity is negative or zero.
+ */
public void setCapacity(int c)
{
if (c<=0)
@@ -430,12 +581,24 @@ public class CacheMap implements Map
} // end setCapacity
+ /**
+ * Returns the shrink percentage of this cache map.
+ *
+ * @return The shrink percentage of this cache map.
+ */
public int getShrinkPercentage()
{
return shrink_percentage;
} // end getShrinkPercentage
+ /**
+ * Sets the shrink percentage of this cache map.
+ *
+ * @param p The new shrink percentage for this cache map.
+ * @exception java.lang.IllegalArgumentException If the specified shrink percentage is not in the
+ * range [1..100].
+ */
public void setShrinkPercentage(int p)
{
if ((p<=0) || (p>100))
@@ -444,12 +607,23 @@ public class CacheMap implements Map
} // end setShrinkPercentage
+ /**
+ * Returns the strategy object associated with the cache map.
+ *
+ * @return The strategy object associated with the cache map.
+ */
public CacheMapStrategy getStrategy()
{
return strategy;
} // end getStrategy
+ /**
+ * Sets the strategy object associated with this cache map.
+ *
+ * @param s The new strategy object to be associated with the cache map.
+ * @exception java.lang.NullPointerException If the strategy object reference is null
.
+ */
public void setStrategy(CacheMapStrategy s)
{
if (s==null)
@@ -463,6 +637,11 @@ public class CacheMap implements Map
*--------------------------------------------------------------------------------
*/
+ /**
+ * Causes the current size of the cache map to shrink by at least the shrink percentage specified
+ * in the constructor or in setShrinkPercentage
. Cached items already reclaimed by the
+ * garbage collector are stripped out first.
+ */
public synchronized void shrink()
{
// Figure out how many elements to remove.
diff --git a/src/com/silverwrist/util/cache/CacheMapEntry.java b/src/com/silverwrist/util/cache/CacheMapEntry.java
index dafd491..6d7d73e 100644
--- a/src/com/silverwrist/util/cache/CacheMapEntry.java
+++ b/src/com/silverwrist/util/cache/CacheMapEntry.java
@@ -20,7 +20,19 @@ package com.silverwrist.util.cache;
import java.lang.ref.*;
import java.util.*;
-final class CacheMapEntry implements Map.Entry
+/**
+ * An entry from the CacheMap
class. This object holds the key, a SoftReference
+ * to the value, the timestamp of its last access, and the number of times this element has been accessed.
+ * The latter two values are frequently used by an object implementing the CacheMapStrategy
+ * interface to compute a "figure of merit" for the entry that decides whether or not it gets discarded.
+ *
+ * @author Eric J. Bowersox <erbo@silcom.com>
+ * @version X
+ * @see CacheMap
+ * @see CacheMapStrategy
+ * @see java.lang.ref.SoftReference
+ */
+public final class CacheMapEntry implements Map.Entry
{
/*--------------------------------------------------------------------------------
* Attributes
@@ -37,6 +49,12 @@ final class CacheMapEntry implements Map.Entry
*--------------------------------------------------------------------------------
*/
+ /**
+ * Creates a new CacheMapEntry
.
+ *
+ * @param key The key for this entry.
+ * @param value The value to be held in this entry.
+ */
CacheMapEntry(Object key, Object value)
{
this.key = key;
@@ -45,6 +63,13 @@ final class CacheMapEntry implements Map.Entry
} // end constructor
+ /**
+ * Creates a new CacheMapEntry
.
+ *
+ * @param key The key for this entry.
+ * @param value The value to be held in this entry.
+ * @param q The ReferenceQueue
to add the new reference generated by this constructor to.
+ */
CacheMapEntry(Object key, Object value, ReferenceQueue q)
{
this.key = key;
@@ -58,6 +83,10 @@ final class CacheMapEntry implements Map.Entry
*--------------------------------------------------------------------------------
*/
+ /**
+ * Called by the garbage collector on an object when garbage collection determines that there are no
+ * more references to the object.
+ */
protected void finalize()
{
key = null;
@@ -71,26 +100,53 @@ final class CacheMapEntry implements Map.Entry
*--------------------------------------------------------------------------------
*/
+ /**
+ * Returns the key corresponding to this entry.
+ *
+ * @return The key corresponding to this entry.
+ */
public final Object getKey()
{
return key;
} // end getKey
+ /**
+ * Returns the value corresponding to this entry. If the value has already been reclaimed by the
+ * garbage collector, the return value is null
.
+ *
+ * @return The value corresponding to this entry.
+ */
public final Object getValue()
{
return value.get();
} // end getValue
+ /**
+ * Replaces the value corresponding to this entry with the specified value.
+ *
+ * @param o The new value to be stored in this entry.
+ * @return The former value stored in this entry. If the value has already been reclaimed by the
+ * garbage collector, the return value is null
.
+ */
public final Object setValue(Object o)
{
Object rc = value.get();
+ value.clear();
value = new SoftReference(o);
return rc;
} // end setValue
+ /**
+ * Compares the specified object with this object for equality. The CacheMap
considers
+ * two entries to be equivalent if they have the same key. (The value is not considered because it
+ * might already have been garbage-collected.)
+ *
+ * @param o The other object to compare to.
+ * @return true
if the specified object is equal to this entry.
+ */
public final boolean equals(Object o)
{
// make sure the other element is a Map.Entry
@@ -106,6 +162,11 @@ final class CacheMapEntry implements Map.Entry
} // end equals
+ /**
+ * Returns the hash code value for this map entry, defined to be the hash code value of the key.
+ *
+ * @return The hash code value for this map entry.
+ */
public final int hashCode()
{
return ((key==null) ? 0 : key.hashCode());
@@ -117,6 +178,14 @@ final class CacheMapEntry implements Map.Entry
*--------------------------------------------------------------------------------
*/
+ /**
+ * Replaces the value corresponding to this entry with the specified value.
+ *
+ * @param o The new value to be stored in this entry.
+ * @param q The ReferenceQueue
to add the freshly-generated reference to.
+ * @return The former value stored in this entry. If the value has already been reclaimed by the
+ * garbage collector, the return value is null
.
+ */
final Object setValue(Object o, ReferenceQueue q)
{
Object rc = value.get();
@@ -126,12 +195,24 @@ final class CacheMapEntry implements Map.Entry
} // end setValue
- final boolean isCleared()
+ /**
+ * Returns true
if the value in this map entry has been cleared by the garbage collector.
+ *
+ * @return true
if the value has been cleared, false
if not.
+ */
+ public final boolean isCleared()
{
return (value.get()==null);
} // end isCleared
+ /**
+ * Returns true if the specified reference is the one contained in this map entry.
+ *
+ * @param r The reference we're trying to match.
+ * @return true/CODE> if this reference is the same as the one contained in the map entry,
+ * false
if not.
+ */
final boolean matchReference(Reference r)
{
if (r instanceof SoftReference)
@@ -141,6 +222,9 @@ final class CacheMapEntry implements Map.Entry
} // end matchReference
+ /**
+ * Clears out all references held by this map entry.
+ */
final void discard()
{
key = null;
@@ -149,24 +233,43 @@ final class CacheMapEntry implements Map.Entry
} // end discard
- final int getHits()
+ /**
+ * Returns the number of times this map entry has been referenced.
+ *
+ * @return The number of times this map entry has been referenced.
+ */
+ public final int getHits()
{
return hits;
} // end getHits
- final long getTimestamp()
+ /**
+ * Returns the timestamp of the most recent reference to this map entry.
+ *
+ * @return The timestamp of the most recent reference to this map entry.
+ */
+ public final long getTimestamp()
{
return timestamp;
} // end getTimestamp
- final long getAge(long tick)
+ /**
+ * Returns the number of milliseconds since this map entry was last referenced.
+ *
+ * @param tick The current time tick of the system in milliseconds.
+ * @return The number of milliseconds since this map entry was last referenced.
+ */
+ public final long getAge(long tick)
{
return (tick - timestamp);
} // end getAge
+ /**
+ * References this entry, which increments its "hit" counter and updates its current time stamp.
+ */
final void touch()
{
hits++;
diff --git a/src/com/silverwrist/util/cache/CacheMapStrategy.java b/src/com/silverwrist/util/cache/CacheMapStrategy.java
index 471e508..fb7aee4 100644
--- a/src/com/silverwrist/util/cache/CacheMapStrategy.java
+++ b/src/com/silverwrist/util/cache/CacheMapStrategy.java
@@ -17,8 +17,25 @@
*/
package com.silverwrist.util.cache;
+/**
+ * An interface which allows external code to define a "strategy" for use with the CacheMap
+ * object, in deciding which entries in the map are to be released.
+ *
+ * @author Eric J. Bowersox <erbo@silcom.com>
+ * @version X
+ * @see CacheMap
+ */
public interface CacheMapStrategy
{
+ /**
+ * Computes a "figure of merit" for a given CacheMapEntry
. This value is used by the
+ * CacheMap
to determine which entries to remove; entries with lower "figure of merit"
+ * values get removed first.
+ *
+ * @param entry The cache map entry to compute a figure of merit for.
+ * @param tick The current time stamp, which may be used in a call to CacheMapEntry.getAge
.
+ * @return The "figure of merit" value.
+ */
public abstract long getEntryValue(CacheMapEntry entry, long tick);
} // end interface CacheMapStrategy