001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.util;
017:
018: import java.lang.ref.Reference;
019: import java.util.Iterator;
020: import java.util.LinkedHashMap;
021: import java.util.Map;
022:
023: /**
024: * A {@link Map} with a fixed maximum size which removes the <cite>least recently used</cite> (LRU)
025: * entry if an entry is added when full. This class implements a simple technique for LRU pooling
026: * of objects.
027: * <p>
028: * Note that this cache is based on hard references, not on {@linkplain java.lang.ref.WeakReference
029: * weak} or {@linkplain java.lang.ref.SoftReference soft references} because especially for server
030: * side applications such caches are often too aggressively cleared.
031: *
032: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/util/LRULinkedHashMap.java $
033: * @version $Id: LRULinkedHashMap.java 22443 2006-10-27 20:47:22Z desruisseaux $
034: * @author Simone Giannecchini
035: * @since 2.3
036: */
037: public final class LRULinkedHashMap extends LinkedHashMap {
038: /**
039: * Serial number for cross-version compatibility.
040: */
041: private static final long serialVersionUID = 2082871604196698140L;
042:
043: /** Default maximum capacity. */
044: private static final int DEFAULT_MAXIMUM_CAPACITY = 100;
045:
046: /** Maximum number of entries for this LRU cache. */
047: private final int maxEntries;
048:
049: /**
050: * Constructs a {@code LRULinkedHashMap} with default initial capacity, maximum capacity
051: * and load factor.
052: */
053: public LRULinkedHashMap() {
054: super ();
055: maxEntries = DEFAULT_MAXIMUM_CAPACITY;
056: }
057:
058: /**
059: * Constructs a {@code LRULinkedHashMap} with default maximum capacity and load factor.
060: *
061: * @param initialCapacity The initial capacity.
062: */
063: public LRULinkedHashMap(int initialCapacity) {
064: super (initialCapacity);
065: maxEntries = DEFAULT_MAXIMUM_CAPACITY;
066: }
067:
068: /**
069: * Constructs a {@code LRULinkedHashMap} with default maximum capacity.
070: *
071: * @param initialCapacity The initial capacity.
072: * @param loadFactor The load factor.
073: */
074: public LRULinkedHashMap(int initialCapacity, float loadFactor) {
075: super (initialCapacity, loadFactor);
076: maxEntries = DEFAULT_MAXIMUM_CAPACITY;
077: }
078:
079: /**
080: * Constructs a {@code LRULinkedHashMap} with default maximum capacity.
081: *
082: * @param initialCapacity The initial capacity.
083: * @param loadFactor The load factor.
084: * @param accessOrder The ordering mode: {@code true} for access-order,
085: * {@code false} for insertion-order.
086: */
087: public LRULinkedHashMap(int initialCapacity, float loadFactor,
088: boolean accessOrder) {
089: super (initialCapacity, loadFactor, accessOrder);
090: maxEntries = DEFAULT_MAXIMUM_CAPACITY;
091: }
092:
093: /**
094: * Constructs a {@code LRULinkedHashMap} with the specified maximum capacity.
095: *
096: * @param initialCapacity The initial capacity.
097: * @param loadFactor The load factor.
098: * @param accessOrder The ordering mode: {@code true} for access-order,
099: * {@code false} for insertion-order.
100: * @param maxEntries Maximum number of entries for this LRU cache.
101: */
102: public LRULinkedHashMap(int initialCapacity, float loadFactor,
103: boolean accessOrder, final int maxEntries) {
104: super (initialCapacity, loadFactor, accessOrder);
105: this .maxEntries = maxEntries;
106: }
107:
108: /**
109: * Constructs a {@code LRULinkedHashMap} with all entries from the specified map.
110: *
111: * @param m the map whose mappings are to be placed in this map.
112: */
113: private LRULinkedHashMap(final Map m) {
114: super (m);
115: maxEntries = DEFAULT_MAXIMUM_CAPACITY;
116: removeExtraEntries();
117: }
118:
119: /**
120: * Constructs a {@code LRULinkedHashMap} with all entries from the specified map
121: * and maximum number of entries.
122: *
123: * @param m the map whose mappings are to be placed in this map.
124: * @param maxEntries Maximum number of entries for this LRU cache.
125: */
126: private LRULinkedHashMap(final Map m, final int maxEntries) {
127: super (m);
128: this .maxEntries = maxEntries;
129: removeExtraEntries();
130: }
131:
132: /**
133: * If there is more entries than the maximum amount, remove extra entries.
134: * <p>
135: * <b>Note:</b> Invoking {@code removeExtraEntries()} after adding all entries in the
136: * {@code LRULinkedHashMap(Map)} constructor is less efficient than just iterating over
137: * the {@code maxEntries} first entries at construction time, but super-class constructor
138: * is more efficient for maps with less than {@code maxEntries}. We assume that this is the
139: * most typical case. In addition, this method would be needed anyway if we add a
140: * {@code setMaximumEntries(int)} method in some future Geotools version.
141: */
142: private void removeExtraEntries() {
143: if (size() > maxEntries) {
144: final Iterator it = entrySet().iterator();
145: for (int c = 0; c < maxEntries; c++) {
146: it.next();
147: }
148: while (it.hasNext()) {
149: it.remove();
150: }
151: }
152: }
153:
154: /**
155: * Returns {@code true} if this map should remove its eldest entry. The default implementation
156: * returns {@code true} if the {@linkplain #size number of entries} in this map has reached the
157: * maximum number of entries specified at construction time.
158: *
159: * @param eldest The least recently inserted entry in the map, or if this is an access-ordered
160: * map, the least recently accessed entry. This is the entry that will be removed it this
161: * method returns {@code true}.
162: * @return {@code true} if the eldest entry should be removed from the map;
163: * {@code false} if it should be retained.
164: */
165: protected boolean removeEldestEntry(final Map.Entry eldest) {
166: // /////////////////////////////////////////////////////////////////////
167: //
168: // Do I have to remove anything?
169: //
170: // If I still am below the desired threshold I just return false nad
171: // that is it.
172: //
173: // /////////////////////////////////////////////////////////////////////
174: return size() > maxEntries;
175: }
176: }
|