/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis.collections;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Iterator;
/**
* <p>
* An implementation of a Map which has a maximum size and uses a Least Recently Used
* algorithm to remove items from the Map when the maximum size is reached and new items are added.
* </p>
*
* <p>
* A synchronized version can be obtained with:
* <code>Collections.synchronizedMap( theMapToSynchronize )</code>
* If it will be accessed by multiple threads, you _must_ synchronize access
* to this Map. Even concurrent get(Object) operations produce indeterminate
* behaviour.
* </p>
*
* <p>
* Unlike the Collections 1.0 version, this version of LRUMap does use a true
* LRU algorithm. The keys for all gets and puts are moved to the front of
* the list. LRUMap is now a subclass of SequencedHashMap, and the "LRU"
* key is now equivalent to LRUMap.getFirst().
* </p>
*
* @since Commons Collections 1.0
*
* @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
* @author <a href="mailto:morgand@apache.org">Morgan Delagrange</a>
*/
public class LRUMap extends SequencedHashMap implements Externalizable {
private int maximumSize = 0;
/**
* Default constructor, primarily for the purpose of
* de-externalization. This constructors sets a default
* LRU limit of 100 keys, but this value may be overridden
* internally as a result of de-externalization.
*/
public LRUMap() {
this( 100 );
}
/**
* Create a new LRUMap with a maximum capacity of <i>i</i>.
* Once <i>i</i> capacity is achieved, subsequent gets
* and puts will push keys out of the map. See .
*
* @param i Maximum capacity of the LRUMap
*/
public LRUMap(int i) {
super( i );
maximumSize = i;
}
/**
* <p>Get the value for a key from the Map. The key
* will be promoted to the Most Recently Used position.
* Note that get(Object) operations will modify
* the underlying Collection. Calling get(Object)
* inside of an iteration over keys, values, etc. is
* currently unsupported.</p>
*
* @param key Key to retrieve
* @return Returns the value. Returns null if the key has a
* null value <i>or</i> if the key has no value.
*/
public Object get(Object key) {
if(!containsKey(key)) return null;
Object value = remove(key);
super.put(key,value);
return value;
}
/**
* <p>Removes the key and its Object from the Map.</p>
*
* <p>(Note: this may result in the "Least Recently Used"
* object being removed from the Map. In that case,
* the removeLRU() method is called. See javadoc for
* removeLRU() for more details.)</p>
*
* @param key Key of the Object to add.
* @param value Object to add
* @return Former value of the key
*/
public Object put( Object key, Object value ) {
int mapSize = size();
Object retval = null;
if ( mapSize >= maximumSize ) {
// don't retire LRU if you are just
// updating an existing key
if (!containsKey(key)) {
// lets retire the least recently used item in the cache
removeLRU();
}
}
retval = super.put(key,value);
return retval;
}
/**
* This method is used internally by the class for
* finding and removing the LRU Object.
*/
protected void removeLRU() {
Object key = getFirstKey();
// be sure to call super.get(key), or you're likely to
// get infinite promotion recursion
Object value = super.get(key);
remove(key);
processRemovedLRU(key,value);
}
/**
* Subclasses of LRUMap may hook into this method to
* provide specialized actions whenever an Object is
* automatically removed from the cache. By default,
* this method does nothing.
*
* @param key key that was removed
* @param value value of that key (can be null)
*/
protected void processRemovedLRU(Object key, Object value) {
}
// Externalizable interface
//-------------------------------------------------------------------------
public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException {
maximumSize = in.readInt();
int size = in.readInt();
for( int i = 0; i < size; i++ ) {
Object key = in.readObject();
Object value = in.readObject();
put(key,value);
}
}
public void writeExternal( ObjectOutput out ) throws IOException {
out.writeInt( maximumSize );
out.writeInt( size() );
for( Iterator iterator = keySet().iterator(); iterator.hasNext(); ) {
Object key = iterator.next();
out.writeObject( key );
// be sure to call super.get(key), or you're likely to
// get infinite promotion recursion
Object value = super.get( key );
out.writeObject( value );
}
}
// Properties
//-------------------------------------------------------------------------
/** Getter for property maximumSize.
* @return Value of property maximumSize.
*/
public int getMaximumSize() {
return maximumSize;
}
/** Setter for property maximumSize.
* @param maximumSize New value of property maximumSize.
*/
public void setMaximumSize(int maximumSize) {
this.maximumSize = maximumSize;
while (size() > maximumSize) {
removeLRU();
}
}
// add a serial version uid, so that if we change things in the future
// without changing the format, we can still deserialize properly.
private static final long serialVersionUID = 2197433140769957051L;
}
|