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 020package org.biojava.utils.io; 021 022import java.lang.ref.ReferenceQueue; 023import java.lang.ref.SoftReference; 024import java.util.AbstractMap; 025import java.util.HashMap; 026import java.util.LinkedList; 027import java.util.Map; 028import 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 037public class SoftHashMap extends AbstractMap { 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 map = new HashMap(); 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 hardCache = new LinkedList(); 055 056 /** Reference queue for cleared SoftReference objects. */ 057 058 private final ReferenceQueue queue = new ReferenceQueue(); 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 Object get(Object key) { 073 074 Object result = null; 075 076 // We get the SoftReference represented by that key 077 078 SoftReference soft_ref = (SoftReference) 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 extends SoftReference { 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(Object k, Object key, ReferenceQueue 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 private void clearGCCollected() { 190 191 SoftValue sv; 192 193 while ((sv = (SoftValue) queue.poll()) != null) { 194 195 map.remove(sv.key); // we can access private data! 196 197 } 198 199 } 200 201 202 203 /** 204 205 * Here we put the key, value pair into the HashMap using a SoftValue 206 207 * object. 208 209 */ 210 211 public synchronized Object put(Object key, Object value) { 212 213 clearGCCollected(); 214 215 if ( DEBUG) 216 System.out.println("putting " + key + " on cache. size: " + size()); 217 return map.put(key, new SoftValue(value, key, queue)); 218 219 } 220 221 222 223 public Object remove(Object key) { 224 225 clearGCCollected(); 226 if ( DEBUG) 227 System.out.println("removing " + key + " from cache. size: " + size()); 228 return map.remove(key); 229 230 } 231 232 233 234 public void clear() { 235 236 synchronized (hardCache){ 237 hardCache.clear(); 238 } 239 240 clearGCCollected(); 241 if ( DEBUG) 242 System.out.println("clearing cache"); 243 map.clear(); 244 245 } 246 247 248 249 public int size() { 250 251 clearGCCollected(); 252 253 return map.size(); 254 255 } 256 257 258 259 public Set entrySet() { 260 261 throw new UnsupportedOperationException(); 262 263 } 264 265}