/* * 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.commons.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> * * @deprecated Moved to map subpackage. Due to be removed in v4.0. * @since Commons Collections 1.0 * @version $Revision: 155406 $ $Date: 2005-02-26 12:55:26 +0000 (Sat, 26 Feb 2005) $ * * @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; }