/*
* FixedSizeForgetfulHashMap.java
*
* Created on December 11, 2000, 2:08 PM
*/
package org.limewire.collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Provides a better-defined replacement policy version of
* {@link ForgetfulHashMap}. Like <code>ForgetfulHashMap</code>, this is a
* mapping that "forgets" keys and values using a FIFO replacement policy, much
* like a cache.
* <p>
* Specifically, <code>FixedsizeForgetfulHashMap</code> allows a
* key to be re-mapped to a different value and then "renews" this key so it
* is the last key to be replaced (done in constant time).
<pre>
FixedsizeForgetfulHashMap<String, String> ffhm =
new FixedsizeForgetfulHashMap<String, String>(3);
ffhm.put("myKey1", "Abby");
ffhm.put("myKey2", "Bob");
ffhm.put("myKey3", "Chris");
System.out.println(ffhm);
ffhm.put("myKey4", "Dan");
System.out.println(ffhm);
ffhm.put("myKey3", "replace");
System.out.println(ffhm);
Output:
{myKey1=Abby, myKey2=Bob, myKey3=Chris}
{myKey2=Bob, myKey3=Chris, myKey4=Dan}
{myKey2=Bob, myKey4=Dan, myKey3=replace}
</pre>
* @author Anurag Singla -- initial version
* @author Christopher Rohrs -- cleaned up and added unit tests
* @author Sam Berlin -- extend LinkedHashMap (adds unimplemented methods, simplifies)
*/
public class FixedsizeForgetfulHashMap<K, V> extends LinkedHashMap<K, V> {
/** Maximum number of elements to be stored in the underlying hashMap */
private final int MAXIMUM_SIZE;
/**
* Create a new instance that holds only the last "size" entries.
*
* @param size the number of entries to hold
* @exception IllegalArgumentException if size is less < 1.
*/
public FixedsizeForgetfulHashMap(int size) {
this(size, (size * 4)/3 + 10, 0.75f);
}
/**
* Create a new instance that holds only the last "size" entries,
* using the given initialCapacity and a loadFactor of 0.75.
*
* @param size the number of entries to hold
* @exception IllegalArgumentException if size is less < 1.
*/
public FixedsizeForgetfulHashMap(int size, int initialCapacity) {
this(size, initialCapacity, 0.75f);
}
/**
* Create a new instance that holds only the last "size" entries, using
* the given initialCapacity & loadFactor.
*
* @param size the number of entries to hold
* @exception IllegalArgumentException if size is less < 1.
*/
public FixedsizeForgetfulHashMap(int size, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
//if size is < 1
if (size < 1)
throw new IllegalArgumentException("invalid size: " + size);
//set the max size to the size specified
MAXIMUM_SIZE = size;
}
/**
* Tests if the map is full.
*
* @return true, if the map is full (ie if adding any other entry will
* lead to removal of some other entry to maintain the fixed-size property
* of the map. Returns false, otherwise
*/
public boolean isFull() {
return size() >= MAXIMUM_SIZE;
}
/**
* Removes the least recently used entry from the map.
* @return A Map.Entry object
* @modifies this.
*/
public Map.Entry<K, V> removeLRUEntry() {
//if there are no elements, return null.
if(isEmpty())
return null;
Iterator<Map.Entry<K, V>> i = entrySet().iterator();
Map.Entry<K, V> value = i.next();
i.remove();
return value;
}
/**
* Returns a shallow copy of this Map instance: the keys and
* values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
@Override
@SuppressWarnings("unchecked")
public FixedsizeForgetfulHashMap<K, V> clone() {
return (FixedsizeForgetfulHashMap<K, V>)super.clone();
}
/**
* Returns true if the eldest entry should be removed.
*/
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > MAXIMUM_SIZE;
}
/**
* Overridden to ensure that remapping a key renews the value in the
* linked list.
*/
@Override
public V put(K key, V value) {
V ret = null;
if(containsKey(key))
ret = remove(key);
super.put(key, value);
return ret;
}
}