001 /*
002 * PDB web development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence. This should
006 * be distributed with the code. If you do not have a copy,
007 * see:
008 *
009 * http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors. These should be listed in @author doc comments.
013 *
014 *
015 * Created on Mar 26, 2009
016 * Created by ap3
017 *
018 */
019
020 package org.biojava3.core.util;
021
022 import java.lang.ref.ReferenceQueue;
023 import java.lang.ref.SoftReference;
024 import java.util.AbstractMap;
025 import java.util.HashMap;
026 import java.util.LinkedList;
027 import java.util.Map;
028 import java.util.Set;
029
030
031 /** A in memory cache using soft references. (can be garbage collected)
032 *
033 * This code is based on: http://java-interview-faqs.blogspot.com/2008/09/building-faster-and-efficient-cache.html
034 * */
035
036
037 public class SoftHashMap<K, V> extends AbstractMap<K, V> {
038
039 public static final boolean DEBUG = false;
040
041
042 public static final int DEFAULT_LIMIT = 1;
043
044 /** The internal HashMap that stores SoftReference to actual data. */
045
046 private final Map<K, SoftReference<V>> map = new HashMap<K, SoftReference<V>>();
047
048 /** Maximum Number of references you dont want GC to collect. */
049
050 private final int max_limit;
051
052 /** The FIFO list of hard references, order of last access. */
053
054 private final LinkedList<V> hardCache = new LinkedList<V>();
055
056 /** Reference queue for cleared SoftReference objects. */
057
058 private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
059
060 public SoftHashMap() {
061
062 this(1000);
063
064 }
065
066 public SoftHashMap(int hardSize) {
067
068 max_limit = hardSize;
069
070 }
071
072 public V get(Object key) {
073
074 V result = null;
075
076 // We get the SoftReference represented by that key
077
078 SoftReference<V> soft_ref = map.get(key);
079
080 if (soft_ref != null) {
081
082 try {
083
084 // From the SoftReference we get the value, which can be
085
086 // null if it was not in the map, or it was removed in
087
088 // the clearGCCollected() method defined below
089
090 result = soft_ref.get();
091
092 if (result == null) {
093
094 // If the value has been garbage collected, remove the
095
096 // entry from the HashMap.
097
098 map.remove(key);
099
100 } else {
101
102 // We now add this object to the beginning of the hard
103
104 // reference queue. One reference can occur more than
105
106 // once, because lookups of the FIFO queue are slow, so
107
108 // we don't want to search through it each time to remove
109
110 // duplicates.
111
112 synchronized (hardCache){
113 hardCache.addFirst(result);
114
115 if (hardCache.size() > max_limit) {
116
117 // Remove the last entry if list greater than MAX_LIMIT
118
119 hardCache.removeLast();
120
121 }
122 }
123
124 }
125 } catch (Exception e){
126 e.printStackTrace();
127 }
128
129 }
130
131 return result;
132
133 }
134
135
136
137 /**
138
139 * We define our own subclass of SoftReference which contains not only the
140
141 * value but also the key to make it easier to find the entry in the HashMap
142
143 * after it's been garbage collected.
144
145 */
146
147 private static class SoftValue<K, V> extends SoftReference<V> {
148
149 private final Object key; // always make data member final
150
151
152
153 /**
154
155 * Did you know that an outer class can access private data members and
156
157 * methods of an inner class? I didn't know that! I thought it was only
158
159 * the inner class who could access the outer class's private
160
161 * information. An outer class can also access private members of an
162
163 * inner class inside its inner class.
164
165 */
166
167 private SoftValue(V k, K key, ReferenceQueue<? super V> q) {
168
169 super(k, q);
170
171 this.key = key;
172
173 }
174
175 }
176
177
178
179 /**
180
181 * Here we go through the ReferenceQueue and remove garbage collected
182
183 * SoftValue objects from the HashMap by looking them up using the
184
185 * SoftValue.key data member.
186
187 */
188
189 @SuppressWarnings("unchecked") // every Reference in queue is stored as a SoftValue
190 private void clearGCCollected() {
191
192 SoftValue<K, V> sv;
193
194 while ((sv = (SoftValue<K, V>) queue.poll()) != null) {
195
196 map.remove(sv.key); // we can access private data!
197
198 }
199
200 }
201
202
203
204 /**
205
206 * Here we put the key, value pair into the HashMap using a SoftValue
207
208 * object.
209
210 */
211
212 public synchronized V put(K key, V value) {
213
214 clearGCCollected();
215
216 if ( DEBUG)
217 System.out.println("putting " + key + " on cache. size: " + size());
218
219 map.put(key, new SoftValue<K, V>(value, key, queue));
220
221 return value;
222
223 }
224
225
226
227 public V remove(Object key) {
228
229 clearGCCollected();
230 if ( DEBUG)
231 System.out.println("removing " + key + " from cache. size: " + size());
232 return map.remove(key).get();
233
234 }
235
236
237
238 public void clear() {
239
240 synchronized (hardCache){
241 hardCache.clear();
242 }
243
244 clearGCCollected();
245 if ( DEBUG)
246 System.out.println("clearing cache");
247 map.clear();
248
249 }
250
251
252
253 public int size() {
254
255 clearGCCollected();
256
257 return map.size();
258
259 }
260
261
262
263 public Set<Map.Entry<K, V>> entrySet() {
264
265 throw new UnsupportedOperationException();
266
267 }
268
269 }