uk.org.retep.util.collections.TimedHashMap Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2008, Peter T Mount
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the retep.org.uk nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.org.retep.util.collections;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
*
* @author peter
*/
public class TimedHashMap
extends HashMap
implements TimedMap
{
static final long serialVersionUID = 6953795673173999149L;
private Map> map;
private long lifetime = 600000L; // 10 minutes
/** Creates a new instance of TimedHashMap */
public TimedHashMap()
{
map = new HashMap>();
}
public TimedHashMap( long lifetime )
{
this();
setLifetime( lifetime );
}
public void setLifetime( long millis )
{
lifetime = millis;
}
public long getLifetime()
{
return lifetime;
}
public V get( Object key )
{
synchronized( this )
{
V ret = null;
TimedHashMapEntry ce = map.get( key );
if( ce != null )
{
long deathTime = ce.getCreated() + lifetime;
if( deathTime > System.currentTimeMillis() )
{
ret = ce.getObject();
}
else
{
remove( key );
}
}
return ret;
}
}
public V put( K key, V value )
{
synchronized( this )
{
V ret = null;
TimedHashMapEntry ce = map.get( key );
if( ce == null )
{
ce = map.put( key, new TimedHashMapEntry( value ) );
if( ce != null )
{
ret = ce.getObject();
}
}
else
{
ret = ce.getObject();
ce.setObject( value );
}
return ret;
}
}
public Collection values()
{
return new CacheCollection( map.values() );
}
/**
* Usually objects are only removed from the cache when they are next
* referenced. However, this method will remove all dead entries. This is
* usually called from a house keeping thread.
*/
public void clearTombstones()
{
synchronized( this )
{
Iterator it = keySet().iterator();
while( it.hasNext() )
{
Object key = it.next();
TimedHashMapEntry ce = (TimedHashMapEntry) super.get( key );
if( ce != null )
{
long deathTime = ce.getCreated() + lifetime;
if( deathTime <= System.currentTimeMillis() )
{
it.remove();
}
}
}
}
}
/**
* Removes all keys that begin with the given prefix
*/
public void clearPrefix( String prefix )
{
synchronized( this )
{
Iterator it = keySet().iterator();
while( it.hasNext() )
{
Object key = it.next();
TimedHashMapEntry ce = (TimedHashMapEntry) super.get( key );
if( ce != null )
{
if( key instanceof String && ((String) key).startsWith( prefix ) )
{
it.remove();
}
}
}
}
}
private class CacheIterator
implements Iterator
{
private Iterator iterator;
public CacheIterator( Collection> col )
{
List l = new ArrayList();
for( TimedHashMapEntry e : col )
{
l.add( e.getObject() );
}
iterator = l.iterator();
}
public CacheIterator( Iterator it )
{
iterator = it;
}
/**
* Returns true if the iteration has more elements. (In other
* words, returns true if next would return an element
* rather than throwing an exception.)
*
* @return true if the iterator has more elements.
*
*/
public boolean hasNext()
{
return iterator.hasNext();
}
/** Returns the next element in the iteration.
*
* @return the next element in the iteration.
* @exception NoSuchElementException iteration has no more elements.
*
*/
@SuppressWarnings( "unchecked" )
public V next()
{
V obj = iterator.next();
if( obj instanceof TimedHashMapEntry )
{
obj = ((TimedHashMapEntry) obj).getObject();
}
return obj;
}
/**
* Removes from the underlying collection the last element returned by the
* iterator (optional operation). This method can be called only once per
* call to next. The behavior of an iterator is unspecified if
* the underlying collection is modified while the iteration is in
* progress in any way other than by calling this method.
*
* @exception UnsupportedOperationException if the remove
* operation is not supported by this Iterator.
*
* @exception IllegalStateException if the next method has not
* yet been called, or the remove method has already
* been called after the last call to the next
* method.
*
*/
public void remove()
{
iterator.remove();
}
}
private class CacheCollection
implements Collection
{
private Collection> col;
public CacheCollection( Collection> aCollection )
{
col = aCollection;
}
/** Ensures that this collection contains the specified element (optional
* operation). Returns true if this collection changed as a
* result of the call. (Returns false if this collection does
* not permit duplicates and already contains the specified element.)
*
* Collections that support this operation may place limitations on what
* elements may be added to this collection. In particular, some
* collections will refuse to add null elements, and others will
* impose restrictions on the type of elements that may be added.
* Collection classes should clearly specify in their documentation any
* restrictions on what elements may be added.
*
* If a collection refuses to add a particular element for any reason
* other than that it already contains the element, it must throw
* an exception (rather than returning false). This preserves
* the invariant that a collection always contains the specified element
* after this call returns.
*
* @param o element whose presence in this collection is to be ensured.
* @return true if this collection changed as a result of the
* call
*
* @throws UnsupportedOperationException add is not supported by
* this collection.
* @throws ClassCastException class of the specified element prevents it
* from being added to this collection.
* @throws NullPointerException if the specified element is null and this
* collection does not support null elements.
* @throws IllegalArgumentException some aspect of this element prevents
* it from being added to this collection.
*
*/
public boolean add( V o )
{
return col.add( new TimedHashMapEntry( o ) );
}
/** Adds all of the elements in the specified collection to this collection
* (optional operation). The behavior of this operation is undefined if
* the specified collection is modified while the operation is in progress.
* (This implies that the behavior of this call is undefined if the
* specified collection is this collection, and this collection is
* nonempty.)
*
* @param c elements to be inserted into this collection.
* @return true if this collection changed as a result of the
* call
*
* @throws UnsupportedOperationException if this collection does not
* support the addAll method.
* @throws ClassCastException if the class of an element of the specified
* collection prevents it from being added to this collection.
* @throws NullPointerException if the specified collection contains one
* or more null elements and this collection does not support null
* elements, or if the specified collection is null.
* @throws IllegalArgumentException some aspect of an element of the
* specified collection prevents it from being added to this
* collection.
* @see #add(Object)
*
*/
@SuppressWarnings( "unchecked" )
public boolean addAll( Collection extends V> c )
{
Iterator extends V> it = c.iterator();
boolean changed = false;
while( it.hasNext() )
{
Object obj = it.next();
if( obj instanceof TimedHashMapEntry )
{
changed |= col.add( (TimedHashMapEntry) obj );
}
else
{
changed |= col.add( new TimedHashMapEntry( (V) obj ) );
}
}
return changed;
}
/** Removes all of the elements from this collection (optional operation).
* This collection will be empty after this method returns unless it
* throws an exception.
*
* @throws UnsupportedOperationException if the clear method is
* not supported by this collection.
*
*/
public void clear()
{
col.clear();
}
/** Returns true if this collection contains the specified
* element. More formally, returns true if and only if this
* collection contains at least one element e such that
* (o==null ? e==null : o.equals(e)).
*
* @param o element whose presence in this collection is to be tested.
* @return true if this collection contains the specified
* element
* @throws ClassCastException if the type of the specified element
* is incompatible with this collection (optional).
* @throws NullPointerException if the specified element is null and this
* collection does not support null elements (optional).
*
*/
public boolean contains( Object o )
{
boolean notFound = true;
Iterator it = col.iterator();
while( notFound && it.hasNext() )
{
TimedHashMapEntry ce = (TimedHashMapEntry) it.next();
notFound = !(ce.getObject().equals( o ));
}
return !notFound;
}
/** Returns true if this collection contains all of the elements
* in the specified collection.
*
* @param c collection to be checked for containment in this collection.
* @return true if this collection contains all of the elements
* in the specified collection
* @throws ClassCastException if the types of one or more elements
* in the specified collection are incompatible with this
* collection (optional).
* @throws NullPointerException if the specified collection contains one
* or more null elements and this collection does not support null
* elements (optional).
* @throws NullPointerException if the specified collection is
* null.
* @see #contains(Object)
*
*/
public boolean containsAll( Collection c )
{
boolean found = true;
Iterator it = c.iterator();
while( found && it.hasNext() )
{
found = contains( it.next() );
}
return found;
}
/** Returns true if this collection contains no elements.
*
* @return true if this collection contains no elements
*
*/
public boolean isEmpty()
{
return col.isEmpty();
}
/** Returns an iterator over the elements in this collection. There are no
* guarantees concerning the order in which the elements are returned
* (unless this collection is an instance of some class that provides a
* guarantee).
*
* @return an Iterator over the elements in this collection
*
*/
public Iterator iterator()
{
return new CacheIterator( col );
}
/** Removes a single instance of the specified element from this
* collection, if it is present (optional operation). More formally,
* removes an element e such that (o==null ? e==null :
* o.equals(e)), if this collection contains one or more such
* elements. Returns true if this collection contained the specified
* element (or equivalently, if this collection changed as a result of the
* call).
*
* @param o element to be removed from this collection, if present.
* @return true if this collection changed as a result of the
* call
*
* @throws ClassCastException if the type of the specified element
* is incompatible with this collection (optional).
* @throws NullPointerException if the specified element is null and this
* collection does not support null elements (optional).
* @throws UnsupportedOperationException remove is not supported by this
* collection.
*
*/
public boolean remove( Object o )
{
boolean run = true;
Iterator it = col.iterator();
while( run && it.hasNext() )
{
if( o.equals( it.next() ) )
{
it.remove();
run = false;
}
}
return !run;
}
/**
* Removes all this collection's elements that are also contained in the
* specified collection (optional operation). After this call returns,
* this collection will contain no elements in common with the specified
* collection.
*
* @param c elements to be removed from this collection.
* @return true if this collection changed as a result of the
* call
*
* @throws UnsupportedOperationException if the removeAll method
* is not supported by this collection.
* @throws ClassCastException if the types of one or more elements
* in this collection are incompatible with the specified
* collection (optional).
* @throws NullPointerException if this collection contains one or more
* null elements and the specified collection does not support
* null elements (optional).
* @throws NullPointerException if the specified collection is
* null.
* @see #remove(Object)
* @see #contains(Object)
*
*/
public boolean removeAll( Collection c )
{
boolean changed = false;
Iterator it = c.iterator();
while( it.hasNext() )
{
Object o = it.next();
boolean removed = false;
do
{
removed = remove( o );
changed |= removed;
} while( removed );
}
return changed;
}
/** Retains only the elements in this collection that are contained in the
* specified collection (optional operation). In other words, removes from
* this collection all of its elements that are not contained in the
* specified collection.
*
* @param c elements to be retained in this collection.
* @return true if this collection changed as a result of the
* call
*
* @throws UnsupportedOperationException if the retainAll method
* is not supported by this Collection.
* @throws ClassCastException if the types of one or more elements
* in this collection are incompatible with the specified
* collection (optional).
* @throws NullPointerException if this collection contains one or more
* null elements and the specified collection does not support null
* elements (optional).
* @throws NullPointerException if the specified collection is
* null.
* @see #remove(Object)
* @see #contains(Object)
*
*/
public boolean retainAll( Collection c )
{
// todo
return false;
}
/** Returns the number of elements in this collection. If this collection
* contains more than Integer.MAX_VALUE elements, returns
* Integer.MAX_VALUE.
*
* @return the number of elements in this collection
*
*/
public int size()
{
return col.size();
}
/** Returns an array containing all of the elements in this collection. If
* the collection makes any guarantees as to what order its elements are
* returned by its iterator, this method must return the elements in the
* same order.
*
* The returned array will be "safe" in that no references to it are
* maintained by this collection. (In other words, this method must
* allocate a new array even if this collection is backed by an array).
* The caller is thus free to modify the returned array.
*
* This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this collection
*
*/
public Object[] toArray()
{
Object ary[] = new Object[ size() ];
Iterator it = col.iterator();
for( int i = 0; it.hasNext(); i++ )
{
ary[i] = it.next();
}
return ary;
}
/** Returns an array containing all of the elements in this collection;
* the runtime type of the returned array is that of the specified array.
* If the collection fits in the specified array, it is returned therein.
* Otherwise, a new array is allocated with the runtime type of the
* specified array and the size of this collection.
*
* If this collection fits in the specified array with room to spare
* (i.e., the array has more elements than this collection), the element
* in the array immediately following the end of the collection is set to
* null. This is useful in determining the length of this
* collection only if the caller knows that this collection does
* not contain any null elements.)
*
* If this collection makes any guarantees as to what order its elements
* are returned by its iterator, this method must return the elements in
* the same order.
*
* Like the toArray method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs
*
* Suppose l is a List known to contain only strings.
* The following code can be used to dump the list into a newly allocated
* array of String:
*
*
* String[] x = (String[]) v.toArray(new String[0]);
*
*
* Note that toArray(new Object[0]) is identical in function to
* toArray().
*
* @param a the array into which the elements of this collection are to be
* stored, if it is big enough; otherwise, a new array of the same
* runtime type is allocated for this purpose.
* @return an array containing the elements of this collection
*
* @throws ArrayStoreException the runtime type of the specified array is
* not a supertype of the runtime type of every element in this
* collection.
* @throws NullPointerException if the specified array is null.
*
*/
public T[] toArray( T[] a )
{
// todo
return a;
}
}
private class TimedHashMapEntry
{
/** Holds value of property object. */
private V object;
private long created;
public TimedHashMapEntry()
{
this( null );
}
public TimedHashMapEntry( V aObject )
{
setObject( aObject );
}
/** Getter for property object.
* @return Value of property object.
*
*/
public V getObject()
{
return this.object;
}
/** Setter for property object.
* @param object New value of property object.
*
*/
public void setObject( V object )
{
this.object = object;
setCreated( System.currentTimeMillis() );
}
/** Getter for property created.
* @return Value of property created.
*
*/
public long getCreated()
{
return created;
}
/** Setter for property created.
* @param created New value of property created.
*
*/
public void setCreated( long aCreated )
{
created = aCreated;
}
}
}