diff --git a/src/java/com/samskivert/util/CountHashMap.java b/src/java/com/samskivert/util/CountHashMap.java index 34bba982..ac1809e6 100644 --- a/src/java/com/samskivert/util/CountHashMap.java +++ b/src/java/com/samskivert/util/CountHashMap.java @@ -18,17 +18,34 @@ package com.samskivert.util; +import java.util.AbstractSet; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; +import java.util.Set; /** * A hashmap that maintains a count for each key. * - * This implementation may change, but I find it useful to inherit all - * the goodness of clear(), keySet(), size(), etc. + * This implementation should change so that we extend AbstractMap, do our + * own hashing, and can use our own Entry class. */ -public class CountHashMap extends HashMap +public class CountHashMap extends HashMap { + public interface Entry extends Map.Entry + { + /** + * Get the value of the entry as an int. + */ + public int getCount (); + + /** + * Set the value of this entry as an int. + * @return the old count + */ + public int setCount (int count); + } + /** * Increment the value associated with the specified key, return * the new value. @@ -41,6 +58,20 @@ public class CountHashMap extends HashMap } val[0] += amount; return val[0]; + + /* Alternate implementation, less hashing on the first increment + * but more garbage created every other time. + * (this whole method would be more optimal if this class were + * rewritten) + * + int[] newVal = new int[] { amount }; + int[] oldVal = put(key, newVal); + if (oldVal != null) { + newVal[0] += oldVal[0]; + return oldVal[0]; + } + return 0; + */ } /** @@ -52,6 +83,23 @@ public class CountHashMap extends HashMap return (val == null) ? 0 : val[0]; } + /** + * Set the count for the specified key. + * + * @return the old count. + */ + public int setCount (K key, int count) + { + int[] val = get(key); + if (val == null) { + put(key, new int[] { count }); + return 0; // old value + } + int oldVal = val[0]; + val[0] = count; + return oldVal; + } + /** * Get the total count for all keys in the map. */ @@ -63,4 +111,125 @@ public class CountHashMap extends HashMap } return count; } + + /** + * Compress the count map- remove entries for which the value is 0. + */ + public void compress () + { + for (Iterator itr = values().iterator(); itr.hasNext(); ) { + if (itr.next()[0] == 0) { + itr.remove(); + } + } + } + + // documentation inherited + public Set> entrySet () + { + // a giant mess of hoop-jumpery so that we can convert each Map.Entry + // returned by the iterator to be a CountEntryImpl + return new CountEntrySet(super.entrySet()); + } + + protected static class CountEntryImpl + implements Entry + { + public CountEntryImpl (Map.Entry entry) + { + _entry = entry; + } + + public boolean equals (Object o) + { + return _entry.equals(o); + } + + public K getKey () + { + return _entry.getKey(); + } + + public int[] getValue () + { + return _entry.getValue(); + } + + public int hashCode () + { + return _entry.hashCode(); + } + + public int[] setValue (int[] value) + { + return _entry.setValue(value); + } + + public int getCount () + { + return getValue()[0]; + } + + public int setCount (int count) + { + int[] val = getValue(); + int oldVal = val[0]; + val[0] = count; + return oldVal; + } + + protected Map.Entry _entry; + } + + protected class CountEntrySet + extends AbstractSet> + { + public CountEntrySet (Set superset) + { + _superset = superset; + } + + public Iterator> iterator () + { + final Iterator> itr = _superset.iterator(); + return new Iterator>() { + public boolean hasNext () + { + return itr.hasNext(); + } + + public Entry next () + { + return new CountEntryImpl(itr.next()); + } + + public void remove () + { + itr.remove(); + } + }; + } + + public boolean contains (Object o) + { + return _superset.contains(o); + } + + public boolean remove (Object o) + { + return _superset.remove(o); + } + + public int size () + { + return CountHashMap.this.size(); + } + + public void clear () + { + CountHashMap.this.clear(); + } + + protected Set _superset; + } }